From d405aa55a589da426ab75470899d30cad228886e Mon Sep 17 00:00:00 2001 From: chengzhinei Date: Fri, 31 May 2024 15:00:00 +0800 Subject: [PATCH] support init freegas (#207) * support init free gas * support prior pack tx * init initFreeAddress * txTracker gp use default gp * start bridge service on testing * Update Makefile * Update docker-compose.yml * update config * and bridge ui * start bridge ui * Update docker-compose.yml * Update docker-compose.yml * Update docker-compose.yml * Update Makefile * opt code * Update docker-compose.yml * Update docker-compose.yml * Update test.bridge.config.toml * add cfg of nonce * Update Makefile * Update test.node.config.toml * Update docker-compose.yml * create table pool.free_gas * rename FreeGasNonce to FreeGasCountPerAddr * Update test.node.config.toml * get addr from input * fix addr * del log * add cfg of free gaslimit * fix lint and json * fix UT * Update test.node.config.toml * Update test.node.config.toml * Update pool.go * and free gas config * fix * add dgp * add AddDynamicGp * fix lint * add GetDynamicGasPrice * fix rpc * test * fix ut * test * log error * fix ut * fix ut * fix ut * fix ut * fix ut * test * test * fix ut * use dgp * fix ut * update error msg * opt code * opt code * opt code * opt code * opt code * Update pool_xlayer.go * Update pool_xlayer.go * Update pool_xlayer.go * opt code * fix lint --------- Co-authored-by: ylsGit --- db/migrations/pool/1004.sql | 7 ++ docs/config-file/node-config-doc.html | 4 +- docs/config-file/node-config-doc.md | 68 ++++++++++++- docs/config-file/node-config-schema.json | 27 +++++ jsonrpc/endpoints_eth.go | 3 + jsonrpc/endpoints_eth_xlayer.go | 7 ++ jsonrpc/mocks/mock_pool.go | 2 + jsonrpc/types/interfaces.go | 1 + pool/apollo_xlayer.go | 57 +++++++++++ pool/config.go | 9 ++ pool/interfaces.go | 2 + pool/pgpoolstorage/pgpoolstorage_xlayer.go | 26 +++++ pool/pool.go | 17 +++- pool/pool_xlayer.go | 111 ++++++++++++++++++++ sequencer/apollo_xlayer.go | 13 +++ sequencer/config.go | 2 + sequencer/interfaces.go | 1 + sequencer/mock_pool.go | 6 ++ sequencer/sequencer.go | 15 +-- sequencer/sequencer_xlayer.go | 15 +++ test/Makefile | 67 ++++++++---- test/config/root-ca-cert | 30 ++++++ test/config/test.bridge.config.toml | 99 ++++++++++++++++++ test/config/test.node.config.toml | 5 + test/config/test.sentinel.config.json | 81 +++++++++++++++ test/docker-compose.yml | 112 ++++++++++++++++++++- 26 files changed, 750 insertions(+), 37 deletions(-) create mode 100644 db/migrations/pool/1004.sql create mode 100644 test/config/root-ca-cert create mode 100644 test/config/test.bridge.config.toml create mode 100644 test/config/test.sentinel.config.json diff --git a/db/migrations/pool/1004.sql b/db/migrations/pool/1004.sql new file mode 100644 index 0000000000..50c35471a9 --- /dev/null +++ b/db/migrations/pool/1004.sql @@ -0,0 +1,7 @@ +-- +migrate Up +CREATE TABLE IF NOT EXISTS pool.free_gas ( + addr varchar NOT NULL PRIMARY KEY +); + +-- +migrate Down +DROP TABLE IF EXISTS pool.free_gas; \ No newline at end of file diff --git a/docs/config-file/node-config-doc.html b/docs/config-file/node-config-doc.html index 06873fa18d..dcdd35fb91 100644 --- a/docs/config-file/node-config-doc.html +++ b/docs/config-file/node-config-doc.html @@ -14,7 +14,7 @@
"300ms"
 

Default: "15s"Type: string

PollMinAllowedGasPriceInterval is the interval to poll the suggested min gas price for a tx


Examples:

"1m"
 
"300ms"
-

Default: 64Type: integer

AccountQueue represents the maximum number of non-executable transaction slots permitted per account


Default: 1024Type: integer

GlobalQueue represents the maximum number of non-executable transaction slots for all accounts


EffectiveGasPrice is the config for the effective gas price calculation
Default: falseType: boolean

Enabled is a flag to enable/disable the effective gas price


Default: 0.25Type: number

L1GasPriceFactor is the percentage of the L1 gas price that will be used as the L2 min gas price


Default: 16Type: integer

ByteGasCost is the gas cost per byte that is not 0


Default: 4Type: integer

ZeroByteGasCost is the gas cost per byte that is 0


Default: 1Type: number

NetProfit is the profit margin to apply to the calculated breakEvenGasPrice


Default: 1.1Type: number

BreakEvenFactor is the factor to apply to the calculated breakevenGasPrice when comparing it with the gasPriceSigned of a tx


Default: 10Type: integer

FinalDeviationPct is the max allowed deviation percentage BreakEvenGasPrice on re-calculation


Default: 0Type: integer

EthTransferGasPrice is the fixed gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error


Default: 0Type: number

EthTransferL1GasPriceFactor is the percentage of L1 gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error


Default: 0.5Type: number

L2GasPriceSuggesterFactor is the factor to apply to L1 gas price to get the suggested L2 gas price used in the
calculations when the effective gas price is disabled (testing/metrics purposes)


Default: 0Type: integer

ForkID is the current fork ID of the chain


Default: ["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]Type: array of string

XLayer config
FreeGasAddress is the default free gas address

Each item of this array must be:


Default: 150000Type: integer

FreeClaimGasLimit is the max gas allowed use to do a free claim


Type: array of string

BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method

Each item of this array must be:


Configuration for RPC service. THis one offers a extended Ethereum JSON-RPC API interface to interact with the node
Default: "0.0.0.0"Type: string

Host defines the network adapter that will be used to serve the HTTP requests


Default: 8545Type: integer

Port defines the port to serve the endpoints via HTTP


Default: "1m0s"Type: string

ReadTimeout is the HTTP server read timeout
check net/http.server.ReadTimeout and net/http.server.ReadHeaderTimeout


Examples:

"1m"
+

Default: 64Type: integer

AccountQueue represents the maximum number of non-executable transaction slots permitted per account


Default: 1024Type: integer

GlobalQueue represents the maximum number of non-executable transaction slots for all accounts


EffectiveGasPrice is the config for the effective gas price calculation
Default: falseType: boolean

Enabled is a flag to enable/disable the effective gas price


Default: 0.25Type: number

L1GasPriceFactor is the percentage of the L1 gas price that will be used as the L2 min gas price


Default: 16Type: integer

ByteGasCost is the gas cost per byte that is not 0


Default: 4Type: integer

ZeroByteGasCost is the gas cost per byte that is 0


Default: 1Type: number

NetProfit is the profit margin to apply to the calculated breakEvenGasPrice


Default: 1.1Type: number

BreakEvenFactor is the factor to apply to the calculated breakevenGasPrice when comparing it with the gasPriceSigned of a tx


Default: 10Type: integer

FinalDeviationPct is the max allowed deviation percentage BreakEvenGasPrice on re-calculation


Default: 0Type: integer

EthTransferGasPrice is the fixed gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error


Default: 0Type: number

EthTransferL1GasPriceFactor is the percentage of L1 gas price returned as effective gas price for txs tha are ETH transfers (0 means disabled)
Only one of EthTransferGasPrice or EthTransferL1GasPriceFactor params can be different than 0. If both params are set to 0, the sequencer will halt and log an error


Default: 0.5Type: number

L2GasPriceSuggesterFactor is the factor to apply to L1 gas price to get the suggested L2 gas price used in the
calculations when the effective gas price is disabled (testing/metrics purposes)


Default: 0Type: integer

ForkID is the current fork ID of the chain


Default: ["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"]Type: array of string

XLayer config
FreeGasAddress is the default free gas address

Each item of this array must be:


Default: 150000Type: integer

FreeClaimGasLimit is the max gas allowed use to do a free claim


Type: array of string

BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method

Each item of this array must be:


Default: falseType: boolean

EnableFreeGasByNonce enable free gas


Type: array of string

FreeGasExAddress is the ex address which can be free gas for the transfer receiver

Each item of this array must be:


Default: 0Type: integer

FreeGasCountPerAddr is the count limit of free gas tx per address


Default: 0Type: integer

FreeGasLimit is the max gas allowed use to do a free gas tx


Configuration for RPC service. THis one offers a extended Ethereum JSON-RPC API interface to interact with the node
Default: "0.0.0.0"Type: string

Host defines the network adapter that will be used to serve the HTTP requests


Default: 8545Type: integer

Port defines the port to serve the endpoints via HTTP


Default: "1m0s"Type: string

ReadTimeout is the HTTP server read timeout
check net/http.server.ReadTimeout and net/http.server.ReadHeaderTimeout


Examples:

"1m"
 
"300ms"
 

Default: "1m0s"Type: string

WriteTimeout is the HTTP server write timeout
check net/http.server.WriteTimeout


Examples:

"1m"
 
"300ms"
@@ -62,7 +62,7 @@
 
"300ms"
 

Metrics is the config for the sequencer metrics
Default: "1h0m0s"Type: string

Interval is the interval of time to calculate sequencer metrics


Examples:

"1m"
 
"300ms"
-

Default: trueType: boolean

EnableLog is a flag to enable/disable metrics logs


StreamServerCfg is the config for the stream server
Default: 0Type: integer

Port to listen on


Default: ""Type: string

Filename of the binary data file


Default: 0Type: integer

Version of the binary data file


Default: 0Type: integer

ChainID is the chain ID


Default: falseType: boolean

Enabled is a flag to enable/disable the data streamer


Log is the log configuration
Default: ""Type: enum (of string)

Must be one of:

  • "production"
  • "development"

Default: ""Type: enum (of string)

Must be one of:

  • "debug"
  • "info"
  • "warn"
  • "error"
  • "dpanic"
  • "panic"
  • "fatal"

Type: array of string

Each item of this array must be:


Default: 0Type: integer

UpgradeEtrogBatchNumber is the batch number of the upgrade etrog


Type: array of string

XLayer config
PackBatchSpacialList is the list of addresses that will have a special gas price

Each item of this array must be:


Default: 0Type: number

GasPriceMultiple is the multiple of the gas price


Default: 0Type: integer

QueryPendingTxsLimit is used to limit amount txs from the db


Configuration of the sequence sender service
Default: "5s"Type: string

WaitPeriodSendSequence is the time the sequencer waits until
trying to send a sequence to L1


Examples:

"1m"
+

Default: trueType: boolean

EnableLog is a flag to enable/disable metrics logs


StreamServerCfg is the config for the stream server
Default: 0Type: integer

Port to listen on


Default: ""Type: string

Filename of the binary data file


Default: 0Type: integer

Version of the binary data file


Default: 0Type: integer

ChainID is the chain ID


Default: falseType: boolean

Enabled is a flag to enable/disable the data streamer


Log is the log configuration
Default: ""Type: enum (of string)

Must be one of:

  • "production"
  • "development"

Default: ""Type: enum (of string)

Must be one of:

  • "debug"
  • "info"
  • "warn"
  • "error"
  • "dpanic"
  • "panic"
  • "fatal"

Type: array of string

Each item of this array must be:


Default: 0Type: integer

UpgradeEtrogBatchNumber is the batch number of the upgrade etrog


Type: array of string

XLayer config
PackBatchSpacialList is the list of addresses that will have a special gas price

Each item of this array must be:


Default: 0Type: number

GasPriceMultiple is the multiple of the gas price


Default: 0Type: number

InitGasPriceMultiple is the multiple of the gas price for init free gas tx


Default: 0Type: integer

QueryPendingTxsLimit is used to limit amount txs from the db


Configuration of the sequence sender service
Default: "5s"Type: string

WaitPeriodSendSequence is the time the sequencer waits until
trying to send a sequence to L1


Examples:

"1m"
 
"300ms"
 

Default: "5s"Type: string

LastBatchVirtualizationTimeMaxWaitPeriod is time since sequences should be sent


Examples:

"1m"
 
"300ms"
diff --git a/docs/config-file/node-config-doc.md b/docs/config-file/node-config-doc.md
index dcd7ddc652..afc70c08a7 100644
--- a/docs/config-file/node-config-doc.md
+++ b/docs/config-file/node-config-doc.md
@@ -709,6 +709,10 @@ SecretKey=""
 | - [FreeGasAddress](#Pool_FreeGasAddress )                                       | No      | array of string | No         | -          | XLayer config
FreeGasAddress is the default free gas address | | - [FreeClaimGasLimit](#Pool_FreeClaimGasLimit ) | No | integer | No | - | FreeClaimGasLimit is the max gas allowed use to do a free claim | | - [BridgeClaimMethodSigs](#Pool_BridgeClaimMethodSigs ) | No | array of string | No | - | BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method | +| - [EnableFreeGasByNonce](#Pool_EnableFreeGasByNonce ) | No | boolean | No | - | EnableFreeGasByNonce enable free gas | +| - [FreeGasExAddress](#Pool_FreeGasExAddress ) | No | array of string | No | - | FreeGasExAddress is the ex address which can be free gas for the transfer receiver | +| - [FreeGasCountPerAddr](#Pool_FreeGasCountPerAddr ) | No | integer | No | - | FreeGasCountPerAddr is the count limit of free gas tx per address | +| - [FreeGasLimit](#Pool_FreeGasLimit ) | No | integer | No | - | FreeGasLimit is the max gas allowed use to do a free gas tx | ### 7.1. `Pool.IntervalToRefreshBlockedAddresses` @@ -1248,6 +1252,53 @@ FreeClaimGasLimit=150000 **Type:** : `array of string` **Description:** BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method +### 7.18. `Pool.EnableFreeGasByNonce` + +**Type:** : `boolean` + +**Default:** `false` + +**Description:** EnableFreeGasByNonce enable free gas + +**Example setting the default value** (false): +``` +[Pool] +EnableFreeGasByNonce=false +``` + +### 7.19. `Pool.FreeGasExAddress` + +**Type:** : `array of string` +**Description:** FreeGasExAddress is the ex address which can be free gas for the transfer receiver + +### 7.20. `Pool.FreeGasCountPerAddr` + +**Type:** : `integer` + +**Default:** `0` + +**Description:** FreeGasCountPerAddr is the count limit of free gas tx per address + +**Example setting the default value** (0): +``` +[Pool] +FreeGasCountPerAddr=0 +``` + +### 7.21. `Pool.FreeGasLimit` + +**Type:** : `integer` + +**Default:** `0` + +**Description:** FreeGasLimit is the max gas allowed use to do a free gas tx + +**Example setting the default value** (0): +``` +[Pool] +FreeGasLimit=0 +``` + ## 8. `[RPC]` **Type:** : `object` @@ -2885,6 +2936,7 @@ CheckLastL2BlockHashOnCloseBatch=true | - [StreamServer](#Sequencer_StreamServer ) | No | object | No | - | StreamServerCfg is the config for the stream server | | - [PackBatchSpacialList](#Sequencer_PackBatchSpacialList ) | No | array of string | No | - | XLayer config
PackBatchSpacialList is the list of addresses that will have a special gas price | | - [GasPriceMultiple](#Sequencer_GasPriceMultiple ) | No | number | No | - | GasPriceMultiple is the multiple of the gas price | +| - [InitGasPriceMultiple](#Sequencer_InitGasPriceMultiple ) | No | number | No | - | InitGasPriceMultiple is the multiple of the gas price for init free gas tx | | - [QueryPendingTxsLimit](#Sequencer_QueryPendingTxsLimit ) | No | integer | No | - | QueryPendingTxsLimit is used to limit amount txs from the db | ### 10.1. `Sequencer.DeletePoolTxsL1BlockConfirmations` @@ -3572,7 +3624,21 @@ PackBatchSpacialList is the list of addresses that will have a special gas price GasPriceMultiple=0 ``` -### 10.11. `Sequencer.QueryPendingTxsLimit` +### 10.11. `Sequencer.InitGasPriceMultiple` + +**Type:** : `number` + +**Default:** `0` + +**Description:** InitGasPriceMultiple is the multiple of the gas price for init free gas tx + +**Example setting the default value** (0): +``` +[Sequencer] +InitGasPriceMultiple=0 +``` + +### 10.12. `Sequencer.QueryPendingTxsLimit` **Type:** : `integer` diff --git a/docs/config-file/node-config-schema.json b/docs/config-file/node-config-schema.json index a6ae0dcc44..5d48e11755 100644 --- a/docs/config-file/node-config-schema.json +++ b/docs/config-file/node-config-schema.json @@ -471,6 +471,28 @@ }, "type": "array", "description": "BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method" + }, + "EnableFreeGasByNonce": { + "type": "boolean", + "description": "EnableFreeGasByNonce enable free gas", + "default": false + }, + "FreeGasExAddress": { + "items": { + "type": "string" + }, + "type": "array", + "description": "FreeGasExAddress is the ex address which can be free gas for the transfer receiver" + }, + "FreeGasCountPerAddr": { + "type": "integer", + "description": "FreeGasCountPerAddr is the count limit of free gas tx per address", + "default": 0 + }, + "FreeGasLimit": { + "type": "integer", + "description": "FreeGasLimit is the max gas allowed use to do a free gas tx", + "default": 0 } }, "additionalProperties": false, @@ -1391,6 +1413,11 @@ "description": "GasPriceMultiple is the multiple of the gas price", "default": 0 }, + "InitGasPriceMultiple": { + "type": "number", + "description": "InitGasPriceMultiple is the multiple of the gas price for init free gas tx", + "default": 0 + }, "QueryPendingTxsLimit": { "type": "integer", "description": "QueryPendingTxsLimit is used to limit amount txs from the db", diff --git a/jsonrpc/endpoints_eth.go b/jsonrpc/endpoints_eth.go index 5cf82066b2..d95a0cba58 100644 --- a/jsonrpc/endpoints_eth.go +++ b/jsonrpc/endpoints_eth.go @@ -1043,6 +1043,9 @@ func (e *EthEndpoints) tryToAddTxToPool(input, ip string) (interface{}, types.Er return RPCErrorResponse(types.InvalidParamsErrorCode, "invalid tx input", err, false) } log.Infof("adding TX to the pool: %v", tx.Hash().Hex()) + + dgp := getDynamicGp(e.cfg.DynamicGP.Enabled, e.dgpMan.lastPrice) + e.pool.AddDynamicGp(dgp) if err := e.pool.AddTx(context.Background(), *tx, ip); err != nil { // it's not needed to log the error here, because we check and log if needed // for each specific case during the "pool.AddTx" internal steps diff --git a/jsonrpc/endpoints_eth_xlayer.go b/jsonrpc/endpoints_eth_xlayer.go index c4f977c29f..2ab478108d 100644 --- a/jsonrpc/endpoints_eth_xlayer.go +++ b/jsonrpc/endpoints_eth_xlayer.go @@ -420,3 +420,10 @@ func (e *EthEndpoints) GetPendingStat() (interface{}, types.Error) { ReadyTxCount: readyTxCount, }, nil } + +func getDynamicGp(enableDgp bool, dgp *big.Int) *big.Int { + if !enableDgp || dgp == nil { + return big.NewInt(0) + } + return dgp +} diff --git a/jsonrpc/mocks/mock_pool.go b/jsonrpc/mocks/mock_pool.go index 7f4e7c2452..a574e797ec 100644 --- a/jsonrpc/mocks/mock_pool.go +++ b/jsonrpc/mocks/mock_pool.go @@ -40,6 +40,8 @@ func (_m *PoolMock) AddTx(ctx context.Context, tx types.Transaction, ip string) return r0 } +func (_m *PoolMock) AddDynamicGp(_ *big.Int) {} + // CalculateEffectiveGasPrice provides a mock function with given fields: rawTx, txGasPrice, txGasUsed, l1GasPrice, l2GasPrice func (_m *PoolMock) CalculateEffectiveGasPrice(rawTx []byte, txGasPrice *big.Int, txGasUsed uint64, l1GasPrice uint64, l2GasPrice uint64) (*big.Int, error) { ret := _m.Called(rawTx, txGasPrice, txGasUsed, l1GasPrice, l2GasPrice) diff --git a/jsonrpc/types/interfaces.go b/jsonrpc/types/interfaces.go index f56127ac44..3d1333c74f 100644 --- a/jsonrpc/types/interfaces.go +++ b/jsonrpc/types/interfaces.go @@ -16,6 +16,7 @@ import ( // PoolInterface contains the methods required to interact with the tx pool. type PoolInterface interface { AddTx(ctx context.Context, tx types.Transaction, ip string) error + AddDynamicGp(dgp *big.Int) GetGasPrices(ctx context.Context) (pool.GasPrices, error) GetNonce(ctx context.Context, address common.Address) (uint64, error) GetPendingTxHashesSince(ctx context.Context, since time.Time) ([]common.Hash, error) diff --git a/pool/apollo_xlayer.go b/pool/apollo_xlayer.go index d912a4a2e6..d0fd26d969 100644 --- a/pool/apollo_xlayer.go +++ b/pool/apollo_xlayer.go @@ -16,6 +16,11 @@ type apolloConfig struct { BridgeClaimMethods []string EnablePendingStat bool + EnableFreeGasByNonce bool + FreeGasExAddress []string + FreeGasCountPerAddr uint64 + FreeGasLimit uint64 + sync.RWMutex } @@ -44,6 +49,14 @@ func (c *apolloConfig) setFreeGasAddresses(freeGasAddrs []string) { copy(c.FreeGasAddresses, freeGasAddrs) } +func (c *apolloConfig) setFreeGasExAddresses(freeGasExAddrs []string) { + if c == nil || !c.EnableApollo { + return + } + c.FreeGasExAddress = make([]string, len(freeGasExAddrs)) + copy(c.FreeGasExAddress, freeGasExAddrs) +} + func (c *apolloConfig) setBridgeClaimMethods(bridgeClaimMethods []string) { if c == nil || !c.EnableApollo { return @@ -66,6 +79,12 @@ func UpdateConfig(apolloConfig Config) { getApolloConfig().setFreeGasAddresses(apolloConfig.FreeGasAddress) getApolloConfig().EnableWhitelist = apolloConfig.EnableWhitelist getApolloConfig().setBridgeClaimMethods(apolloConfig.BridgeClaimMethodSigs) + + getApolloConfig().EnableFreeGasByNonce = apolloConfig.EnableFreeGasByNonce + getApolloConfig().setFreeGasExAddresses(apolloConfig.FreeGasExAddress) + getApolloConfig().FreeGasCountPerAddr = apolloConfig.FreeGasCountPerAddr + getApolloConfig().FreeGasLimit = apolloConfig.FreeGasLimit + getApolloConfig().Unlock() } @@ -95,6 +114,44 @@ func isFreeGasAddress(localFreeGasAddrs []string, address common.Address) bool { return contains(localFreeGasAddrs, address) } +func getEnableFreeGasByNonce(enableFreeGasByNonce bool) bool { + if getApolloConfig().enable() { + getApolloConfig().RLock() + defer getApolloConfig().RUnlock() + return getApolloConfig().EnableFreeGasByNonce + } + + return enableFreeGasByNonce +} + +func isFreeGasExAddress(localFreeGasExAddrs []string, address common.Address) bool { + if getApolloConfig().enable() { + getApolloConfig().RLock() + defer getApolloConfig().RUnlock() + return contains(getApolloConfig().FreeGasExAddress, address) + } + + return contains(localFreeGasExAddrs, address) +} + +func getFreeGasCountPerAddr(localFreeGasCountPerAddr uint64) uint64 { + if getApolloConfig().enable() { + getApolloConfig().RLock() + defer getApolloConfig().RUnlock() + return getApolloConfig().FreeGasCountPerAddr + } + return localFreeGasCountPerAddr +} + +func getFreeGasLimit(localFreeGasLimit uint64) uint64 { + if getApolloConfig().enable() { + getApolloConfig().RLock() + defer getApolloConfig().RUnlock() + return getApolloConfig().FreeGasLimit + } + return localFreeGasLimit +} + func getGlobalQueue(globalQueue uint64) uint64 { if getApolloConfig().enable() { getApolloConfig().RLock() diff --git a/pool/config.go b/pool/config.go index b6733f1a48..64c9137516 100644 --- a/pool/config.go +++ b/pool/config.go @@ -57,6 +57,15 @@ type Config struct { FreeClaimGasLimit uint64 `mapstructure:"FreeClaimGasLimit"` // BridgeClaimMethodSignature for tracking BridgeClaimMethodSignature method BridgeClaimMethodSigs []string `mapstructure:"BridgeClaimMethodSigs"` + + // EnableFreeGasByNonce enable free gas + EnableFreeGasByNonce bool `mapstructure:"EnableFreeGasByNonce"` + // FreeGasExAddress is the ex address which can be free gas for the transfer receiver + FreeGasExAddress []string `mapstructure:"FreeGasExAddress"` + // FreeGasCountPerAddr is the count limit of free gas tx per address + FreeGasCountPerAddr uint64 `mapstructure:"FreeGasCountPerAddr"` + // FreeGasLimit is the max gas allowed use to do a free gas tx + FreeGasLimit uint64 `mapstructure:"FreeGasLimit"` } // EffectiveGasPriceCfg contains the configuration properties for the effective gas price diff --git a/pool/interfaces.go b/pool/interfaces.go index 9008a31754..b902b3a77c 100644 --- a/pool/interfaces.go +++ b/pool/interfaces.go @@ -44,6 +44,8 @@ type storage interface { GetInnerTx(ctx context.Context, txHash common.Hash) (string, error) UpdateReadyTxCount(ctx context.Context, count uint64) error GetReadyTxCount(ctx context.Context) (uint64, error) + AddFreeGasAddr(ctx context.Context, addr common.Address) error + IsFreeGasAddr(ctx context.Context, addr common.Address) (bool, error) } type stateInterface interface { diff --git a/pool/pgpoolstorage/pgpoolstorage_xlayer.go b/pool/pgpoolstorage/pgpoolstorage_xlayer.go index 40cf651f97..6e76ec0a7d 100644 --- a/pool/pgpoolstorage/pgpoolstorage_xlayer.go +++ b/pool/pgpoolstorage/pgpoolstorage_xlayer.go @@ -2,6 +2,7 @@ package pgpoolstorage import ( "context" + "database/sql" "errors" "time" @@ -98,3 +99,28 @@ func (p *PostgresPoolStorage) GetReadyTxCount(ctx context.Context) (uint64, erro return count, nil } + +// IsFreeGasAddr determines if the address is free gas or +// not. +func (p *PostgresPoolStorage) IsFreeGasAddr(ctx context.Context, addr common.Address) (bool, error) { + var exists bool + req := "SELECT exists (SELECT 1 FROM pool.free_gas WHERE addr = $1)" + err := p.db.QueryRow(ctx, req, addr.String()).Scan(&exists) + if err != nil && err != sql.ErrNoRows { + return false, err + } + + return exists, nil +} + +// AddFreeGasAddr add free gas address +func (p *PostgresPoolStorage) AddFreeGasAddr(ctx context.Context, addr common.Address) error { + sql := `INSERT INTO pool.free_gas(addr) VALUES ($1)` + + _, err := p.db.Exec(ctx, sql, addr.String()) + if err != nil { + return err + } + + return nil +} diff --git a/pool/pool.go b/pool/pool.go index 8ac72acce6..b912f86c29 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -51,6 +51,8 @@ type Pool struct { gasPrices GasPrices gasPricesMux *sync.RWMutex effectiveGasPrice *EffectiveGasPrice + dynamicGasPrice *big.Int + dgpMux *sync.RWMutex } type preExecutionResponse struct { @@ -87,6 +89,7 @@ func NewPool(cfg Config, batchConstraintsCfg state.BatchConstraintsCfg, s storag gasPrices: GasPrices{0, 0}, gasPricesMux: new(sync.RWMutex), effectiveGasPrice: NewEffectiveGasPrice(cfg.EffectiveGasPrice), + dgpMux: new(sync.RWMutex), } p.refreshGasPrices() go func(cfg *Config, p *Pool) { @@ -535,8 +538,12 @@ func (p *Pool) validateTx(ctx context.Context, poolTx Transaction) error { } } - // Reject transactions with a gas price lower than the minimum gas price - if !isFreeGasAddress(p.cfg.FreeGasAddress, from) || !poolTx.IsClaims { // XLayer handle + freeGp, err := p.checkFreeGp(ctx, poolTx, from) + if err != nil { + return err + } + + if !freeGp { // XLayer handle p.minSuggestedGasPriceMux.RLock() gasPriceCmp := poolTx.GasPrice().Cmp(p.minSuggestedGasPrice) if gasPriceCmp == -1 { @@ -604,6 +611,12 @@ func (p *Pool) validateTx(ctx context.Context, poolTx Transaction) error { return err } + if getEnableFreeGasByNonce(p.cfg.EnableFreeGasByNonce) { + if err := p.checkAndUpdateFreeGasAddr(ctx, poolTx, from, lastL2Block.Root()); err != nil { + return err + } + } + return nil } diff --git a/pool/pool_xlayer.go b/pool/pool_xlayer.go index 6944dbcd14..b086479ea1 100644 --- a/pool/pool_xlayer.go +++ b/pool/pool_xlayer.go @@ -2,9 +2,13 @@ package pool import ( "context" + "fmt" + "math/big" + "strings" "sync" "time" + "github.com/0xPolygonHermez/zkevm-node/hex" "github.com/0xPolygonHermez/zkevm-node/log" "github.com/ethereum/go-ethereum/common" ) @@ -14,6 +18,17 @@ const ( BridgeClaimMethodSignature = "0xccaa2d11" // BridgeClaimMessageMethodSignature for tracking BridgeClaimMethodSignature method BridgeClaimMessageMethodSignature = "0xf5efcd79" + //ExWithdrawalMethodSignature erc20 contract transfer(address recipient, uint256 amount) + ExWithdrawalMethodSignature = "0xa9059cbb" + + // TestnetChainID the chain id of xlayer testnet + TestnetChainID = 195 + // MainnetChainID the chain id of xlayer mainnet + MainnetChainID = 196 + // TestnetBridgeURL the bridge url od xlayer testnet + TestnetBridgeURL = "https://www.okx.com/xlayer/bridge-test" + // MainnetBridgeURL the bridge url od xlayer mainnet + MainnetBridgeURL = "https://www.okx.com/xlayer/bridge" ) func contains(s []string, ele common.Address) bool { @@ -84,3 +99,99 @@ func (p *Pool) GetMinSuggestedGasPriceWithDelta(ctx context.Context, delta time. return p.storage.MinL2GasPriceSince(ctx, fromTimestamp) } + +// GetDynamicGasPrice returns the current L2 dynamic gas price +func (p *Pool) GetDynamicGasPrice() *big.Int { + p.dgpMux.RLock() + dgp := p.dynamicGasPrice + p.dgpMux.RUnlock() + if dgp == nil || dgp.Cmp(big.NewInt(0)) == 0 { + _, l2Gp := p.GetL1AndL2GasPrice() + dgp = new(big.Int).SetUint64(l2Gp) + } + return dgp +} + +func (p *Pool) checkFreeGp(ctx context.Context, poolTx Transaction, from common.Address) (bool, error) { + if isFreeGasAddress(p.cfg.FreeGasAddress, from) && poolTx.IsClaims { // claim tx + return true, nil + } + if getEnableFreeGasByNonce(p.cfg.EnableFreeGasByNonce) && poolTx.GasPrice().Cmp(big.NewInt(0)) == 0 { // free-gas tx by count + isFreeAddr, err := p.storage.IsFreeGasAddr(ctx, from) + if err != nil { + log.Errorf("failed to check free gas address from storage: %v", err) + return false, err + } + + freeGasCountPerAddrConfig := getFreeGasCountPerAddr(p.cfg.FreeGasCountPerAddr) + if isFreeAddr { + if poolTx.Nonce() < freeGasCountPerAddrConfig { + if poolTx.Gas() > getFreeGasLimit(p.cfg.FreeGasLimit) { + return false, fmt.Errorf("gas-free transaction with too high gas limit") + } + return true, nil + } else { + return false, fmt.Errorf("you are no longer eligible for gas-free transactions because for each new address, only the first %d transactions(address nonce less than %d) can be gas-free", + freeGasCountPerAddrConfig, + freeGasCountPerAddrConfig) + } + } else { + bridgeURL := "" + switch p.chainID { + case TestnetChainID: + bridgeURL = TestnetBridgeURL + case MainnetChainID: + bridgeURL = MainnetBridgeURL + } + return false, fmt.Errorf("you are unable to initiate a gas-free transaction from this address unless you have previously transferred funds to this address via the X Layer Bridge (%s) or the OKX Exchange, and only the first %d transactions(address nonce less than %d)", + bridgeURL, + freeGasCountPerAddrConfig, + freeGasCountPerAddrConfig) + } + } + return false, nil +} + +func (p *Pool) checkAndUpdateFreeGasAddr(ctx context.Context, poolTx Transaction, from common.Address, root common.Hash) error { + // check and store the free gas address + var freeGpAddr common.Address + inputHex := hex.EncodeToHex(poolTx.Data()) + // hard code + if isFreeGasExAddress(p.cfg.FreeGasExAddress, from) && + strings.HasPrefix(inputHex, ExWithdrawalMethodSignature) && + len(inputHex) > 74 { // erc20 contract transfer + addrHex := "0x" + inputHex[10:74] + freeGpAddr = common.HexToAddress(addrHex) + } else if poolTx.IsClaims && + len(inputHex) > 4554 { // bridge contract claim + addrHex := "0x" + inputHex[4490:4554] + freeGpAddr = common.HexToAddress(addrHex) + } + + if freeGpAddr.Cmp(common.Address{}) != 0 { + nonce, err := p.state.GetNonce(ctx, freeGpAddr, root) + if err != nil { + log.Errorf("failed to get nonce while adding tx to the pool", err) + return err + } + if nonce < getFreeGasCountPerAddr(p.cfg.FreeGasCountPerAddr) { + if err = p.storage.AddFreeGasAddr(ctx, freeGpAddr); err != nil { + log.Errorf("failed to save free gas address to the storage", err) + return err + } + } + } + return nil +} + +// AddDynamicGp cache the dynamic gas price of L2 +func (p *Pool) AddDynamicGp(dgp *big.Int) { + _, l2Gp := p.GetL1AndL2GasPrice() + result := new(big.Int).SetUint64(l2Gp) + if result.Cmp(dgp) < 0 { + result = new(big.Int).Set(dgp) + } + p.dgpMux.Lock() + p.dynamicGasPrice = result + p.dgpMux.Unlock() +} diff --git a/sequencer/apollo_xlayer.go b/sequencer/apollo_xlayer.go index b3bce074a5..ac690aa149 100644 --- a/sequencer/apollo_xlayer.go +++ b/sequencer/apollo_xlayer.go @@ -13,6 +13,7 @@ type ApolloConfig struct { FullBatchSleepDuration types.Duration PackBatchSpacialList []string GasPriceMultiple float64 + InitGasPriceMultiple float64 QueryPendingTxsLimit uint64 sync.RWMutex @@ -42,6 +43,7 @@ func UpdateConfig(apolloConfig Config) { getApolloConfig().FullBatchSleepDuration = apolloConfig.Finalizer.FullBatchSleepDuration getApolloConfig().PackBatchSpacialList = apolloConfig.PackBatchSpacialList getApolloConfig().GasPriceMultiple = apolloConfig.GasPriceMultiple + getApolloConfig().InitGasPriceMultiple = apolloConfig.InitGasPriceMultiple getApolloConfig().QueryPendingTxsLimit = apolloConfig.QueryPendingTxsLimit getApolloConfig().Unlock() } @@ -85,6 +87,17 @@ func getGasPriceMultiple(gpMul float64) float64 { return ret } +func getInitGasPriceMultiple(gpMul float64) float64 { + ret := gpMul + if getApolloConfig().Enable() { + getApolloConfig().RLock() + defer getApolloConfig().RUnlock() + ret = getApolloConfig().InitGasPriceMultiple + } + + return ret +} + func getQueryPendingTxsLimit(limit uint64) uint64 { ret := limit if getApolloConfig().Enable() { diff --git a/sequencer/config.go b/sequencer/config.go index a2a80e9278..5f0857232c 100644 --- a/sequencer/config.go +++ b/sequencer/config.go @@ -36,6 +36,8 @@ type Config struct { PackBatchSpacialList []string `mapstructure:"PackBatchSpacialList"` // GasPriceMultiple is the multiple of the gas price GasPriceMultiple float64 `mapstructure:"GasPriceMultiple"` + // InitGasPriceMultiple is the multiple of the gas price for init free gas tx + InitGasPriceMultiple float64 `mapstructure:"InitGasPriceMultiple"` // QueryPendingTxsLimit is used to limit amount txs from the db QueryPendingTxsLimit uint64 `mapstructure:"QueryPendingTxsLimit"` } diff --git a/sequencer/interfaces.go b/sequencer/interfaces.go index 7b25b14071..cd696856c6 100644 --- a/sequencer/interfaces.go +++ b/sequencer/interfaces.go @@ -31,6 +31,7 @@ type txPool interface { GetEarliestProcessedTx(ctx context.Context) (common.Hash, error) CountPendingTransactions(ctx context.Context) (uint64, error) UpdateReadyTxCount(ctx context.Context, count uint64) error + GetDynamicGasPrice() *big.Int } // ethermanInterface contains the methods required to interact with ethereum. diff --git a/sequencer/mock_pool.go b/sequencer/mock_pool.go index 22d9b44056..539d20a90b 100644 --- a/sequencer/mock_pool.go +++ b/sequencer/mock_pool.go @@ -4,6 +4,7 @@ package sequencer import ( context "context" + "math/big" common "github.com/ethereum/go-ethereum/common" @@ -123,6 +124,11 @@ func (_m *PoolMock) GetEarliestProcessedTx(ctx context.Context) (common.Hash, er return r0, r1 } +// GetDynamicGasPrice returns the current L2 dynamic gas price +func (_m *PoolMock) GetDynamicGasPrice() *big.Int{ + return big.NewInt(0) +} + // GetGasPrices provides a mock function with given fields: ctx func (_m *PoolMock) GetGasPrices(ctx context.Context) (pool.GasPrices, error) { ret := _m.Called(ctx) diff --git a/sequencer/sequencer.go b/sequencer/sequencer.go index 73a18fabae..f4bbd5259d 100644 --- a/sequencer/sequencer.go +++ b/sequencer/sequencer.go @@ -239,14 +239,17 @@ func (s *Sequencer) addTxToWorker(ctx context.Context, tx pool.Transaction) erro return err } - addrs := getPackBatchSpacialList(s.cfg.PackBatchSpacialList) - if addrs[txTracker.FromStr] { - txTracker.IsClaimTx = true - _, l2gp := s.pool.GetL1AndL2GasPrice() - defaultGp := new(big.Int).SetUint64(l2gp) + freeGp, isClaimTx := s.checkFreeGas(tx, txTracker) + if freeGp { + defaultGp := s.pool.GetDynamicGasPrice() baseGp := s.worker.getBaseClaimGp(defaultGp) copyBaseGp := new(big.Int).Set(baseGp) - txTracker.GasPrice = copyBaseGp.Mul(copyBaseGp, new(big.Int).SetUint64(uint64(getGasPriceMultiple(s.cfg.GasPriceMultiple)))) + if isClaimTx { + txTracker.IsClaimTx = true + txTracker.GasPrice = copyBaseGp.Mul(copyBaseGp, new(big.Int).SetUint64(uint64(getGasPriceMultiple(s.cfg.GasPriceMultiple)))) + } else { + txTracker.GasPrice = defaultGp.Mul(defaultGp, new(big.Int).SetUint64(uint64(getInitGasPriceMultiple(s.cfg.InitGasPriceMultiple)))) + } } replacedTx, dropReason := s.worker.AddTxTracker(ctx, txTracker) diff --git a/sequencer/sequencer_xlayer.go b/sequencer/sequencer_xlayer.go index 50d5749ddf..838c9a8e4e 100644 --- a/sequencer/sequencer_xlayer.go +++ b/sequencer/sequencer_xlayer.go @@ -2,9 +2,11 @@ package sequencer import ( "context" + "math/big" "time" "github.com/0xPolygonHermez/zkevm-node/log" + "github.com/0xPolygonHermez/zkevm-node/pool" pmetric "github.com/0xPolygonHermez/zkevm-node/sequencer/metrics" "github.com/0xPolygonHermez/zkevm-node/state" ) @@ -33,3 +35,16 @@ func (s *Sequencer) updateReadyTxCount() { func (s *Sequencer) countReadyTx() { state.InfiniteSafeRun(s.updateReadyTxCount, "error counting ready tx", time.Second) } + +func (s *Sequencer) checkFreeGas(tx pool.Transaction, txTracker *TxTracker) (freeGp, claimTx bool) { + // check if tx is init-free-gas and if it can be prior pack + freeGp = tx.GasPrice().Cmp(big.NewInt(0)) == 0 + + // check if tx is bridge-claim + addrs := getPackBatchSpacialList(s.cfg.PackBatchSpacialList) + if addrs[txTracker.FromStr] { + claimTx = true + } + + return +} diff --git a/test/Makefile b/test/Makefile index 833ba703af..5c124b9df8 100644 --- a/test/Makefile +++ b/test/Makefile @@ -11,7 +11,6 @@ DOCKERCOMPOSESTATEDB := xlayer-state-db DOCKERCOMPOSEPOOLDB := xlayer-pool-db DOCKERCOMPOSEEVENTDB := xlayer-event-db DOCKERCOMPOSEDACDB := xlayer-data-availability-db -DOCKERCOMPOSENETWORK := xlayer-mock-l1-network DOCKERCOMPOSEEXPLORERL1 := xlayer-explorer-l1 DOCKERCOMPOSEEXPLORERL1DB := xlayer-explorer-l1-db DOCKERCOMPOSEEXPLORERL2 := xlayer-explorer-l2 @@ -26,37 +25,24 @@ DOCKERCOMPOSEPERMISSIONLESSZKPROVER := xlayer-permissionless-prover DOCKERCOMPOSENODEAPPROVE := xlayer-approve DOCKERCOMPOSEMETRICS := xlayer-metrics DOCKERCOMPOSEAPPSEQV1TOV2 := xlayer-sequencer-v1tov2 -DOCKERCOMPOSEAPPSEQSENDER := xlayer-sequence-sender DOCKERCOMPOSEAPPSEQSENDERV1TOV2 := xlayer-sequence-sender-v1tov2 -DOCKERCOMPOSEAPPL2GASP := xlayer-l2gaspricer DOCKERCOMPOSEAPPL2GASPV1TOV2 := xlayer-l2gaspricer-v1tov2 -DOCKERCOMPOSEAPPAGG := xlayer-aggregator DOCKERCOMPOSEAPPAGGV1TOV2 := xlayer-aggregator-v1tov2 -DOCKERCOMPOSEAPPRPC := xlayer-json-rpc DOCKERCOMPOSEAPPRPCV1TOV2 := xlayer-json-rpc-v1tov2 -DOCKERCOMPOSEAPPSYNC := xlayer-sync DOCKERCOMPOSEAPPSYNCV1TOV2 := xlayer-sync-v1tov2 -DOCKERCOMPOSEAPPETHTXMANAGER := xlayer-eth-tx-manager DOCKERCOMPOSEAPPETHTXMANAGERV1TOV2 := xlayer-eth-tx-manager-v1tov2 -DOCKERCOMPOSESTATEDB := xlayer-state-db -DOCKERCOMPOSEPOOLDB := xlayer-pool-db -DOCKERCOMPOSEEVENTDB := xlayer-event-db DOCKERCOMPOSENETWORK := xlayer-mock-l1-network DOCKERCOMPOSEV1TOV2NETWORK := xlayer-v1tov2-l1-network -DOCKERCOMPOSEEXPLORERL1 := xlayer-explorer-l1 -DOCKERCOMPOSEEXPLORERL1DB := xlayer-explorer-l1-db -DOCKERCOMPOSEEXPLORERL2 := xlayer-explorer-l2 -DOCKERCOMPOSEEXPLORERL2DB := xlayer-explorer-l2-db -DOCKERCOMPOSEEXPLORERRPC := xlayer-explorer-json-rpc -DOCKERCOMPOSEZKPROVER := xlayer-prover -DOCKERCOMPOSEPERMISSIONLESSDB := xlayer-permissionless-db -DOCKERCOMPOSEPERMISSIONLESSNODE := xlayer-permissionless-node -DOCKERCOMPOSEPERMISSIONLESSZKPROVER := xlayer-permissionless-prover -DOCKERCOMPOSENODEAPPROVE := xlayer-approve DOCKERCOMPOSENODEAPPROVEV1TOV2 := xlayer-approve-v1tov2 -DOCKERCOMPOSEMETRICS := xlayer-metrics DOCKERCOMPOSEGRAFANA := grafana +DOCKER_COMPOSE_BRIDGE_DB := xlayer-bridge-db +DOCKER_COMPOSE_BRIDGE := xlayer-bridge-service +DOCKER_COMPOSE_REDIS := xlayer-bridge-redis +DOCKER_COMPOSE_COIN_KAFKA_NODE := xlayer-bridge-coin-kafka +DOCKER_COMPOSE_ZOOKEEPER := kafka-zookeeper +DOCKER_COMPOSE_BRIDGE_UI := xlayer-bridge-ui + RUNSTATEDB := $(DOCKERCOMPOSE) up -d $(DOCKERCOMPOSESTATEDB) RUNPOOLDB := $(DOCKERCOMPOSE) up -d $(DOCKERCOMPOSEPOOLDB) RUNEVENTDB := $(DOCKERCOMPOSE) up -d $(DOCKERCOMPOSEEVENTDB) @@ -98,6 +84,13 @@ RUNV1TOV2APPROVE := $(DOCKERCOMPOSE) up -d $(DOCKERCOMPOSENODEAPPROVEV1TOV2) RUNMETRICS := $(DOCKERCOMPOSE) up -d $(DOCKERCOMPOSEMETRICS) +RUN_BRIDGE_DB := $(DOCKERCOMPOSE) up -d $(DOCKER_COMPOSE_BRIDGE_DB) +RUN_BRIDGE := $(DOCKERCOMPOSE) up -d $(DOCKER_COMPOSE_BRIDGE) +RUN_BRIDGE_UI := $(DOCKERCOMPOSE) up -d $(DOCKER_COMPOSE_BRIDGE_UI) +RUN_REDIS := $(DOCKERCOMPOSE) up -d $(DOCKER_COMPOSE_REDIS) +RUN_COIN_KAFKA := $(DOCKERCOMPOSE) up -d $(DOCKER_COMPOSE_COIN_KAFKA_NODE) +RUN_ZOOKEEPER := $(DOCKERCOMPOSE) up -d $(DOCKER_COMPOSE_ZOOKEEPER) + RUN := $(DOCKERCOMPOSE) up -d STOPSTATEDB := $(DOCKERCOMPOSE) stop $(DOCKERCOMPOSESTATEDB) && $(DOCKERCOMPOSE) rm -f $(DOCKERCOMPOSESTATEDB) @@ -651,6 +644,38 @@ stop-approve-pol: ## Stops approve in node container stop-approve-pol-v1tov2: ## Stops approve in node container $(STOPV1TOV2APPROVE) +.PHONY: run-bridge +run-bridge: ## Runs a full node whit bridge services + $(RUNSTATEDB) + $(RUNPOOLDB) + $(RUNEVENTDB) + $(RUNDACDB) + $(RUN_BRIDGE_DB) + $(RUN_REDIS) + $(RUN_COIN_KAFKA) + $(RUN_ZOOKEEPER) + $(RUNL1NETWORK) + $(RUNEXPLORERL1DB) + $(RUNEXPLORERL2DB) + sleep 1 + $(RUNZKPROVER) + $(RUNAPPROVE) + sleep 3 + $(RUNSYNC) + sleep 4 + $(RUNDACNODE) + $(RUNETHTXMANAGER) + $(RUNSEQUENCER) + $(RUNSEQUENCESENDER) + $(RUNL2GASPRICER) + $(RUNAGGREGATOR) + $(RUNJSONRPC) + $(RUN_BRIDGE) + $(RUN_BRIDGE_UI) + $(RUNEXPLORERJSONRPC) + $(RUNEXPLORERL1) + $(RUNEXPLORERL2) + .PHONY: run run: ## Runs a full node $(RUNSTATEDB) diff --git a/test/config/root-ca-cert b/test/config/root-ca-cert new file mode 100644 index 0000000000..1ec4f0ca43 --- /dev/null +++ b/test/config/root-ca-cert @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFKjCCAxICCQCdkV+iL/cBTzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwH +QWxpYmFiYTERMA8GA1UEAwwIQWxpS2Fma2EwIBcNMjIwNTExMTAzOTMxWhgPMjEy +MjA0MTcxMDM5MzFaMFYxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAw +DgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMREwDwYDVQQDDAhBbGlL +YWZrYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL315apERcpAkDAB +SY4A2bGrRZO4CXj4nvqbwEZ50f1HlwABjzUMKXES7lWrOwrnqZjSIgm5woqu+Pr4 +sWhKFHN19SSnjeKilQoL8SzMk0p22QJK2sqKRMuHtoBtL6uOT+ykV16IEg0fY2Uu +/oX/sF2LAVCIl1IGc2HVKUr56c0/mM6V6Ur5Sum7ctKk2dm6YS5gwDOXcqAaZhwd +jVzqLEW8hmsMS7n+d2/NIJMqXvTHDRQ74xhR9tN2w92keEBGOQoMG/Qw0RvS1aQi +RKpNpvCE7z543istYuFbFji646u6kRCr7I2i4RwV0qXVM1djcS+PysUsIX4mEjdP +Kq0Fptzsii3aeTFuNswOlo5GieE3psVoymIP2HWd6xmlmFaX3Z8Nd4PxA6h0uRIY +tRbLkHw8WfOAl4dXxWQFkbvNYNLRB5xZUYjm3CA+ZhYfJRtNlPa2247Psnbup6CH +k3DP+aExdLmbtyugZO/lNqi9WMZ0qLFGXZDz8astgJPGKiCjihccpP1cdzGlCzGu +iE6S25JEBuXPl4wg4GXNuCg6tcEKL2qinvbrCimrilWuFajBh7hRH0dgkhezw6xU ++3++ZCebEJOXZ8byn3v/gmyx2PDnKlBPcXCy23nadbiX/zpNvNvCqAewajm9AlWY +fXbCl5TkUnyMPsh0rwWeeRYR2kM3AgMBAAEwDQYJKoZIhvcNAQELBQADggIBADxW +YJoWh9DVtwFGp8TOrlbZ7kwflKFv8Hew4SX00K5GwKgmnn3fjdR0F8rZ2ar/BqdD +zR63sv9LGjMci9NWAqPqN5MyKB97KrFV6nHzcYLRmT+ltolqcfp5MeGCqka7ZTEL +t658xxaSXNEY9HGHYskIu7mWd41KAj0RLRJnEEOrCSZzfpzG4LdD6J0u7wpyJSYL +jGxi2xswt5C0x790LS/JmFq65c/vzfATjbmu6XSO3UvtsADpj0pH3FJFhLzoT67o +NrUeFEHrzsMc7JenYmPIYmEb4xXlfctjCzLaiNG3u8uKwXGBk/oagAwXCsI8I0pR +wtW/QedXxlFtUfATRZnI/eLqvJ5cQ6aXg/GyJtAv+ccFf004K1ER00ECe738WNXm ++6NNkhN5gPhwsfoDhq+a7Zmvj9+x/XDjSRqZ8j+XIMi9ZQjTwUAg9JmnhyR4eJXn +oQAxGc3ii98YoAspKZGRX6LoRfYbNE3TXJsSzGw73+PqS1y74xNNmMx2XX6IV/53 +Is5mA8fli6BIEKkAgE6Pn0t6v5EP6haVF84vJazYRIlYflR2mi8p8dU6kohiC79C +e4seRTTZgyXU+5dgFIXqagub2A79tRtPAr+4Xi84jzY84ceUwqX2fxRwkfaUUJb8 +Hh2q+P+VJeK50B83DZ4ui+WNJbAaAbcLMsn/idX3 +-----END CERTIFICATE----- \ No newline at end of file diff --git a/test/config/test.bridge.config.toml b/test/config/test.bridge.config.toml new file mode 100644 index 0000000000..faf093d36c --- /dev/null +++ b/test/config/test.bridge.config.toml @@ -0,0 +1,99 @@ +[Log] +Level = "debug" +Outputs = ["stdout"] + +[Apollo] +Enabled = false +AppID = "xlayer-bridge-service" +Cluster = "default" +MetaAddress = "http://127.0.0.1:8080" +Namespaces = ["application"] +Secret = "" +IsBackupConfig = true + +[SyncDB] +Database = "postgres" +User = "test_user" +Password = "test_password" +Name = "test_db" +Host = "xlayer-bridge-db" +Port = "5432" +MaxConns = 20 + +[ClaimTxManager] +Enabled = true +FrequencyToMonitorTxs = "1s" +PrivateKey = {Path = "/pk/keystore.claimtxmanager", Password = "testonly"} +RetryInterval = "1s" +RetryNumber = 10 +FreeGas = true +AuthorizedClaimMessageAddresses = ["0x90F79bf6EB2c4f870365E785982E1f101E93b906"] + +[Etherman] +L1URL = "http://xlayer-mock-l1-network:8545" +L2URLs = ["http://xlayer-json-rpc:8123"] +L1ChainId = 1337 +L2ChainIds = [195] + +[Synchronizer] +SyncInterval = "1s" +SyncChunkSize = 100 + +[BridgeController] +Store = "postgres" +Height = 32 + +[BridgeServer] +GRPCPort = "9090" +HTTPPort = "8080" +CacheSize = 100000 +DefaultPageLimit = 25 +MaxPageLimit = 100 +BridgeVersion = "v1" +SentinelConfigFilePath = "/app/sentinel_config.json" +[BridgeServer.DB] +Database = "postgres" +User = "test_user" +Password = "test_password" +Name = "test_db" +Host = "xlayer-bridge-db" +Port = "5432" +MaxConns = 20 +TableSuffix = "" +[BridgeServer.Redis] +IsClusterMode = false +Addrs = ["xlayer-bridge-redis:6379"] +Username = "" +Password = "" +DB = 0 +MockPrice = true + +[CoinKafkaConsumer] +Brokers = ["xlayer-bridge-coin-kafka:9092"] +Topics = ["explorer_chainAddressPrice_push"] +ConsumerGroupID = "xlayer-bridge-service" +InitialOffset = -1 +Username = "" +Password = "" +RootCAPath = "/app/root-ca-cert" + +[MessagePushProducer] +Enabled = false + +[NetworkConfig] +GenBlockNumber = 374 +PolygonBridgeAddress = "0x1089Af36bD72553008FAd0A1240B4D5641208494" +PolygonZkEVMGlobalExitRootAddress = "0xB8cedD4B9eF683f0887C44a6E4312dC7A6e2fcdB" +PolygonRollupManagerAddress = "0x2d42E2899662EFf08b13eeb65b154b904C7a1c8a" +PolygonZkEvmAddress = "0xeb173087729c88a47568AF87b17C653039377BA6" +L2PolygonBridgeAddresses = ["0x1089Af36bD72553008FAd0A1240B4D5641208494"] + +[NacosConfig] +NacosUrls = "" +NamespaceId = "public" +ApplicationName = "" +ExternalListenAddr = "127.0.0.1:26659" + +[BusinessConfig] +StandardChainIds = [195] +InnerChainIds = [19500] diff --git a/test/config/test.node.config.toml b/test/config/test.node.config.toml index dd58a86008..633f1dad20 100644 --- a/test/config/test.node.config.toml +++ b/test/config/test.node.config.toml @@ -29,6 +29,8 @@ Outputs = ["stderr"] MaxSHA256Hashes = 1596 [Pool] +FreeGasCountPerAddr = 3 +FreeGasLimit = 1500000 FreeClaimGasLimit = 1500000 IntervalToRefreshBlockedAddresses = "5m" EnableWhitelist = false @@ -76,6 +78,8 @@ WriteTimeout = "60s" MaxRequestsPerIPAndSecond = 5000 SequencerNodeURI = "" EnableL2SuggestedGasPricePolling = true +BatchRequestsEnabled = true +BatchRequestsLimit = 20 GasLimitFactor = 1 DisableAPIs = [] [RPC.WebSockets] @@ -103,6 +107,7 @@ L1SynchronizationMode = "sequential" ApplyAfterNumRollupReceived = 10 [Sequencer] +InitGasPriceMultiple = 1 DeletePoolTxsL1BlockConfirmations = 100 DeletePoolTxsCheckInterval = "12h" TxLifetimeCheckInterval = "10m" diff --git a/test/config/test.sentinel.config.json b/test/config/test.sentinel.config.json new file mode 100644 index 0000000000..2b756b95e7 --- /dev/null +++ b/test/config/test.sentinel.config.json @@ -0,0 +1,81 @@ +{ + "flowRules": [ + { + "resource": "/bridge.v1.BridgeService/CheckAPI", + "controlBehavior": 0, + "threshold": 10 + }, + { + "resource": "/bridge.v1.BridgeService/GetBridges", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetProof", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetBridge", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetClaims", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetTokenWrapped", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetCoinPrice", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetMainCoins", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetPendingTransactions", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetAllTransactions", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetSmtProof", + "controlBehavior": 1, + "threshold": 50, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetNotReadyTransactions", + "controlBehavior": 1, + "threshold": 1, + "maxQueueingTimeMs": 3000 + }, + { + "resource": "/bridge.v1.BridgeService/GetMonitoredTxsByStatus", + "controlBehavior": 1, + "threshold": 1, + "maxQueueingTimeMs": 3000 + } + ] +} \ No newline at end of file diff --git a/test/docker-compose.yml b/test/docker-compose.yml index af2fd80715..67cf47fe3c 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -368,7 +368,7 @@ services: xlayer-explorer-l1: container_name: xlayer-explorer-l1 - image: hermeznetwork/xlayer-explorer:latest + image: hermeznetwork/zkevm-explorer:latest ports: - 4000:4000 environment: @@ -401,13 +401,13 @@ services: xlayer-explorer-l2: container_name: xlayer-explorer-l2 - image: hermeznetwork/xlayer-explorer:latest + image: hermeznetwork/zkevm-explorer:latest ports: - 4001:4000 environment: - NETWORK=POE - - SUBNETWORK=Polygon Hermez - - COIN=ETH + - SUBNETWORK=Local XLayer + - COIN=OKB - ETHEREUM_JSONRPC_VARIANT=geth - ETHEREUM_JSONRPC_HTTP_URL=http://xlayer-explorer-json-rpc:8124 - DATABASE_URL=postgres://l2_explorer_user:l2_explorer_password@xlayer-explorer-l2-db:5432/l2_explorer_db @@ -699,7 +699,7 @@ services: xlayer-data-availability-db: container_name: xlayer-data-availability-db - image: postgres + image: postgres:15 ports: - 5438:5432 environment: @@ -725,3 +725,105 @@ services: - "/bin/sh" - "-c" - "/app/xlayer-signer http -cfg /app/config.toml" + + xlayer-bridge-db: + container_name: xlayer-bridge-db + image: postgres:15 + deploy: + resources: + limits: + memory: 2G + reservations: + memory: 1G + ports: + - 5439:5432 + environment: + - POSTGRES_USER=test_user + - POSTGRES_PASSWORD=test_password + - POSTGRES_DB=test_db + command: [ "postgres", "-N", "500" ] + + xlayer-bridge-redis: + container_name: xlayer-bridge-redis + image: redis + deploy: + resources: + limits: + memory: 2G + reservations: + memory: 1G + expose: + - 6379 + ports: + - 6379:6379 + environment: + - REDIS_PASSWORD=my_password + - REDIS_PORT=6379 + - REDIS_DATABASES=8 + + kafka-zookeeper: + image: wurstmeister/zookeeper + container_name: kafka-zookeeper + ports: + - "2181:2181" + expose: + - 2181 + + xlayer-bridge-coin-kafka: + image: wurstmeister/kafka + container_name: xlayer-bridge-coin-kafka + expose: + - 9092 + environment: + KAFKA_ADVERTISED_LISTENERS: INSIDE://:9092,OUTSIDE://localhost:9123 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT + KAFKA_LISTENERS: INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9123 + KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE + KAFKA_ZOOKEEPER_CONNECT: kafka-zookeeper:2181 + KAFKA_CREATE_TOPICS: "explorer_chainAddressPrice_push:1:1" + + xlayer-bridge-service: + container_name: xlayer-bridge-service + image: xlayer-bridge-service + ports: + - 8080:8080 + - 9090:9090 + environment: + - ZKEVM_BRIDGE_DATABASE_USER=test_user + - ZKEVM_BRIDGE_DATABASE_PASSWORD=test_password + - ZKEVM_BRIDGE_DATABASE_NAME=test_db + - ZKEVM_BRIDGE_DATABASE_HOST=xlayer-bridge-db + - ZKEVM_BRIDGE_DATABASE_PORT=5432 + volumes: + - ./sequencer.keystore:/pk/keystore.claimtxmanager + - ./config/test.bridge.config.toml:/app/config.toml + - ./config/test.sentinel.config.json:/app/sentinel_config.json + - ./config/root-ca-cert:/app/root-ca-cert + command: + - "/bin/sh" + - "-c" + - "/app/xlayer-bridge run --cfg /app/config.toml" + + xlayer-bridge-ui: + container_name: xlayer-bridge-ui + image: hermeznetwork/zkevm-bridge-ui:etrog-v2 + ports: + - 8090:80 + environment: + - ETHEREUM_RPC_URL=http://3.113.237.222:18545 + - ETHEREUM_EXPLORER_URL=http://3.113.237.222:4000 + - ETHEREUM_ROLLUP_MANAGER_ADDRESS=0x2d42E2899662EFf08b13eeb65b154b904C7a1c8a + - ETHEREUM_BRIDGE_CONTRACT_ADDRESS=0x1089Af36bD72553008FAd0A1240B4D5641208494 + - ETHEREUM_FORCE_UPDATE_GLOBAL_EXIT_ROOT=true + - ETHEREUM_PROOF_OF_EFFICIENCY_CONTRACT_ADDRESS=0xeb173087729c88a47568AF87b17C653039377BA6 + - POLYGON_ZK_EVM_RPC_URL=http://3.113.237.222:8123 + - POLYGON_ZK_EVM_EXPLORER_URL=http://3.113.237.222:4001 + - POLYGON_ZK_EVM_BRIDGE_CONTRACT_ADDRESS=0x1089Af36bD72553008FAd0A1240B4D5641208494 + - POLYGON_ZK_EVM_NETWORK_ID=1 + - BRIDGE_API_URL=http://3.113.237.222:8080 + - ENABLE_FIAT_EXCHANGE_RATES=false + - ENABLE_OUTDATED_NETWORK_MODAL=false + - ENABLE_DEPOSIT_WARNING=true + - ENABLE_REPORT_FORM=false + - USE_FIAT_EXCHANGE_RATES=false + - SHOW_OUTDATED_NETWORK_MODAL=false \ No newline at end of file