Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate mock signer service from xlayer-node #9

Merged
merged 9 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,14 @@ build-rust:
build-go:
$(GOENVVARS) go build -ldflags "all=$(LDFLAGS)" -o $(GOBIN)/$(GOBINARY) $(GOCMD)

.PHONY: build-docker
build-docker: ## Builds a docker image with the cdk binary
.PHONY: build-docker
build-docker: build-mock-signer-docker ## Builds a docker image with the cdk binary
docker build -t cdk -f ./Dockerfile .

.PHONY: build-mock-signer-docker
build-mock-signer-docker:
docker build --progress=plain -t xlayer-signer -f ./test/signer/Dockerfile .

.PHONY: build-docker-nc
build-docker-nc: ## Builds a docker image with the cdk binary - but without build cache
docker build --no-cache=true -t cdk -f ./Dockerfile .
Expand Down
42 changes: 42 additions & 0 deletions etherman/etherman_xlayer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package etherman

import (
"crypto/ecdsa"
"math/big"

"github.com/0xPolygon/cdk/log"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)

// LoadAuthFromKeyStoreXLayer loads an authorization from a key store file
func (etherMan *Client) LoadAuthFromKeyStoreXLayer(path, password string) (
*bind.TransactOpts,
*ecdsa.PrivateKey,
error,
) {
auth, pk, err := newAuthFromKeystoreXLayer(path, password, etherMan.l1Cfg.L1ChainID)
if err != nil {
return nil, nil, err
}

log.Infof("loaded authorization for address: %v", auth.From.String())
etherMan.auth[auth.From] = auth
return &auth, pk, nil
}

// newAuthFromKeystoreXLayer an authorization instance from a keystore file
func newAuthFromKeystoreXLayer(path, password string, chainID uint64) (bind.TransactOpts, *ecdsa.PrivateKey, error) {
log.Infof("reading key from: %v", path)
key, err := newKeyFromKeystore(path, password)
if err != nil {
return bind.TransactOpts{}, nil, err
}
if key == nil {
return bind.TransactOpts{}, nil, nil
}
auth, err := bind.NewKeyedTransactorWithChainID(key.PrivateKey, new(big.Int).SetUint64(chainID))
if err != nil {
return bind.TransactOpts{}, nil, err
}
return *auth, key.PrivateKey, nil
}
87 changes: 87 additions & 0 deletions etherman/mock_etherman.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package etherman

import (
"errors"
"fmt"
"math/big"

polygonzkevm "github.com/0xPolygon/cdk-contracts-tooling/contracts/banana/polygonvalidiumetrog"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

// BuildMockSequenceBatchesTxData builds a []bytes to be sent to the PoE SC method SequenceBatches.
func (etherMan *Client) BuildMockSequenceBatchesTxData(sender common.Address,
validiumBatchData []polygonzkevm.PolygonValidiumEtrogValidiumBatchData,
maxSequenceTimestamp uint64,
l2Coinbase common.Address,
dataAvailabilityMessage []byte,
l1InfoTreeLeafCount uint32,
expectedInputHash string,
) (to *common.Address, data []byte, err error) {
opts, err := etherMan.getAuthByAddress(sender)
if errors.Is(err, ErrNotFound) {
return nil, nil, fmt.Errorf("failed to build sequence batches, err: %w", ErrPrivateKeyNotFound)
}
opts.NoSend = true
// force nonce, gas limit and gas price to avoid querying it from the chain
opts.Nonce = big.NewInt(1)
opts.GasLimit = uint64(1)
opts.GasPrice = big.NewInt(1)

tx, err := etherMan.sequenceMockBatches(
opts,
validiumBatchData,
maxSequenceTimestamp,
l2Coinbase,
dataAvailabilityMessage,
l1InfoTreeLeafCount,
expectedInputHash,
)
if err != nil {
return nil, nil, err
}

return tx.To(), tx.Data(), nil
}

func (etherMan *Client) sequenceMockBatches(opts bind.TransactOpts,
validiumBatchData []polygonzkevm.PolygonValidiumEtrogValidiumBatchData,
maxSequenceTimestamp uint64,
l2Coinbase common.Address,
dataAvailabilityMessage []byte,
l1InfoTreeLeafCount uint32,
expectedInputHash string,
) (*types.Transaction, error) {
var tx *types.Transaction
var err error

var finalInputHash = [32]byte{}
// Request will a hex string beginning with "0x...", so strip first 2 chars.
for i, bb := range common.Hex2Bytes(expectedInputHash[2:]) {
finalInputHash[i] = bb
}

tx, err = etherMan.Contracts.Banana.Rollup.SequenceBatchesValidium(
&opts,
validiumBatchData,
l1InfoTreeLeafCount,
maxSequenceTimestamp,
finalInputHash,
l2Coinbase,
dataAvailabilityMessage,
)

if err != nil {
if parsedErr, ok := TryParseError(err); ok {
err = parsedErr
}
err = fmt.Errorf(
"error sequencing batches: %w, dataAvailabilityMessage: %s",
err, common.Bytes2Hex(dataAvailabilityMessage),
)
}

return tx, err
}
45 changes: 45 additions & 0 deletions test/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,48 @@ services:
- "postgres"
- "-N"
- "500"

xlayer-mock-l1-network:
container_name: xlayer-mock-l1-network
image: zjg555543/geth:fork13-v0.0.3
ports:
- 8545:8545
- 8546:8546
command:
- "--http"
- "--http.api"
- "admin,eth,debug,miner,net,txpool,personal,web3"
- "--http.addr"
- "0.0.0.0"
- "--http.corsdomain"
- "*"
- "--http.vhosts"
- "*"
- "--ws"
- "--ws.origins"
- "*"
- "--ws.addr"
- "0.0.0.0"
- "--dev"
- "--dev.period"
- "1"
- "--datadir"
- "/geth_data"
- "--syncmode"
- "full"
- "--rpc.allow-unprotected-txs"

xlayer-signer:
container_name: xlayer-signer
restart: unless-stopped
image: xlayer-signer:latest # assuming you build docker image using `make build-mock-signer-docker`
ports:
- 7001:7001
volumes:
- ../test/signer/signer.config.toml:/app/config.toml
- ./sequencer.keystore:/pk/sequencer.keystore
- ./aggregator.keystore:/pk/aggregator.keystore
command:
- "/bin/sh"
- "-c"
- "/app/xlayer-signer http -cfg /app/config.toml"
19 changes: 19 additions & 0 deletions test/signer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# CONTAINER FOR BUILDING BINARY
FROM golang:1.22 AS build

# INSTALL DEPENDENCIES
RUN go install github.com/gobuffalo/packr/v2/packr2@v2.8.3
COPY go.mod go.sum /src/
RUN cd /src && go mod download

# BUILD BINARY
COPY . /src
RUN cd /src/db && packr2
RUN cd /src/test/signer && make build

# CONTAINER FOR RUNNING BINARY
FROM alpine:3.18.0
COPY --from=build /src/test/signer/dist/xlayer-signer /app/xlayer-signer

EXPOSE 7001
CMD ["/bin/sh", "-c", "/app/xlayer-signer"]
52 changes: 52 additions & 0 deletions test/signer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
ARCH := $(shell arch)

ifeq ($(ARCH),x86_64)
ARCH = amd64
else
ifeq ($(ARCH),aarch64)
ARCH = arm64
endif
endif
GOBASE := $(shell pwd)
GOBIN := $(GOBASE)/dist
GOENVVARS := GOBIN=$(GOBIN) CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH)
GOBINARY := xlayer-signer
GOCMD := $(GOBASE)

LDFLAGS += -X 'github.com/0xPolygonHermez/zkevm-node.Version=$(VERSION)'
LDFLAGS += -X 'github.com/0xPolygonHermez/zkevm-node.GitRev=$(GITREV)'
LDFLAGS += -X 'github.com/0xPolygonHermez/zkevm-node.GitBranch=$(GITBRANCH)'
LDFLAGS += -X 'github.com/0xPolygonHermez/zkevm-node.BuildDate=$(DATE)'

# Check dependencies
# Check for Go
.PHONY: check-go
check-go:
@which go > /dev/null || (echo "Error: Go is not installed" && exit 1)

# Targets that require the checks
http: check-go

arguments := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))

.PHONY: http
http: ## Runs the signer server
go run main.go http -cfg config/signer.config.toml

.PHONY: build
build: ## Builds the binary locally into ./dist
$(GOENVVARS) go build -ldflags "all=$(LDFLAGS)" -o $(GOBIN)/$(GOBINARY) $(GOCMD)

## Help display.
## Pulls comments from beside commands and prints a nicely formatted
## display with the commands and their usage information.
.DEFAULT_GOAL := help

.PHONY: help
help: ## Prints this help
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \
| sort \
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

.DEFAULT:
@echo ""
103 changes: 103 additions & 0 deletions test/signer/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package config

import (
"bytes"
"errors"
"path/filepath"
"strings"

"github.com/0xPolygon/cdk/config/types"
"github.com/0xPolygon/cdk/log"
"github.com/ethereum/go-ethereum/common"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"github.com/urfave/cli/v2"
)

const (
// FlagCfg is the flag for cfg
FlagCfg = "cfg"
)

// L1 is the default configuration values
type L1 struct {
ChainID uint64 `mapstructure:"ChainId"`
RPC string `mapstructure:"RPC"`
SeqPrivateKey types.KeystoreFileConfig `mapstructure:"SeqPrivateKey"`
AggPrivateKey types.KeystoreFileConfig `mapstructure:"AggPrivateKey"`

PolygonMaticAddress common.Address `mapstructure:"PolygonMaticAddress"`
GlobalExitRootManagerAddr common.Address `mapstructure:"GlobalExitRootManagerAddress"`
DataCommitteeAddr common.Address `mapstructure:"DataCommitteeAddress"`
PolygonZkEVMAddress common.Address `mapstructure:"PolygonZkEVMAddress"`
PolygonRollupManagerAddress common.Address `mapstructure:"PolygonRollupManagerAddress"`
}

// Config is the configuration for the tool
type Config struct {
Port int `mapstructure:"Port"`
L1 L1 `mapstructure:"L1"`
Log log.Config `mapstructure:"Log"`
}

// Default parses the default configuration values.
func Default() (*Config, error) {
var cfg Config
viper.SetConfigType("toml")

err := viper.ReadConfig(bytes.NewBuffer([]byte(DefaultValues)))
if err != nil {
return nil, err
}
err = viper.Unmarshal(&cfg, viper.DecodeHook(mapstructure.TextUnmarshallerHookFunc()))
if err != nil {
return nil, err
}
return &cfg, nil
}

// Load parses the configuration values from the config file and environment variables
func Load(ctx *cli.Context) (*Config, error) {
cfg, err := Default()
if err != nil {
return nil, err
}
configFilePath := ctx.String(FlagCfg)
if configFilePath != "" {
dirName, fileName := filepath.Split(configFilePath)

fileExtension := strings.TrimPrefix(filepath.Ext(fileName), ".")
fileNameWithoutExtension := strings.TrimSuffix(fileName, "."+fileExtension)

viper.AddConfigPath(dirName)
viper.SetConfigName(fileNameWithoutExtension)
viper.SetConfigType(fileExtension)
}
viper.AutomaticEnv()
replacer := strings.NewReplacer(".", "_")
viper.SetEnvKeyReplacer(replacer)
viper.SetEnvPrefix("ZKEVM_DATA_STREAMER")
err = viper.ReadInConfig()
if err != nil {
var fileNotFoundErr *viper.ConfigFileNotFoundError
if errors.As(err, fileNotFoundErr) {
log.Infof("config file not found")
} else {
log.Infof("error reading config file: ", err)
return nil, err
}
}

decodeHooks := []viper.DecoderConfigOption{
// this allows arrays to be decoded from env var separated by ",", example: MY_VAR="value1,value2,value3"
viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
mapstructure.TextUnmarshallerHookFunc(), mapstructure.StringToSliceHookFunc(","))),
}

err = viper.Unmarshal(&cfg, decodeHooks...)
if err != nil {
return nil, err
}

return cfg, nil
}
21 changes: 21 additions & 0 deletions test/signer/config/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package config

// DefaultValues is the default configuration
const DefaultValues = `
Port = 8080

[L1]
ChainId = 11155111
RPC = "https://rpc.ankr.com/eth_sepolia/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
PolygonZkEVMAddress = "0x812cB73e48841a6736bB94c65c56341817cE6304"
GlobalExitRootManagerAddr = "0x0e9Bb928351a50227ebFEC9782Db005Ba9b6C052"
DataCommitteeAddr = "0x246EcFCae4423631c9eE3A86DE37F77BCF27FAaE"
PolygonMaticAddress = "0xe223519d64C0A49e7C08303c2220251be6b70e1d"
SeqPrivateKey = {Path = "../../test/sequencer.keystore", Password = "testonly"}
AggPrivateKey = {Path = "../../test/aggregator.keystore", Password = "testonly"}

[Log]
Environment = "development"
Level = "debug"
Outputs = ["stdout"]
`
Loading
Loading