diff --git a/config/default.go b/config/default.go index 254f4695b4..b065fbfa43 100644 --- a/config/default.go +++ b/config/default.go @@ -99,6 +99,10 @@ MaxGasPriceLimit = 0 OperateAmount = 0 RequestSignURI = "/priapi/v1/assetonchain/ecology/ecologyOperate" QuerySignURI = "/priapi/v1/assetonchain/ecology/querySignDataByOrderNo" + [EthTxManager.HTTP] + Enable = false + Host = "0.0.0.0" + Port = "7001" [RPC] Host = "0.0.0.0" diff --git a/docs/config-file/node-config-doc.html b/docs/config-file/node-config-doc.html index e825ebd042..71f0f692cc 100644 --- a/docs/config-file/node-config-doc.html +++ b/docs/config-file/node-config-doc.html @@ -4,7 +4,7 @@
"300ms"
 

Type: array of object

PrivateKeys defines all the key store files that are going
to be read in order to provide the private keys to sign the L1 txs

Each item of this array must be:

Type: string

Path is the file path for the key store file


Type: string

Password is the password to decrypt the key store file



Default: 0Type: integer

ForcedGas is the amount of gas to be forced in case of gas estimation error


Default: 1Type: number

GasPriceMarginFactor is used to multiply the suggested gas price provided by the network
in order to allow a different gas price to be set for all the transactions and making it
easier to have the txs prioritized in the pool, default value is 1.

ex:
suggested gas price: 100
GasPriceMarginFactor: 1
gas price = 100

suggested gas price: 100
GasPriceMarginFactor: 1.1
gas price = 110


Default: 0Type: integer

MaxGasPriceLimit helps avoiding transactions to be sent over an specified
gas price amount, default value is 0, which means no limit.
If the gas price provided by the network and adjusted by the GasPriceMarginFactor
is greater than this configuration, transaction will have its gas price set to
the value configured in this config as the limit.

ex:

suggested gas price: 100
gas price margin factor: 20%
max gas price limit: 150
tx gas price = 120

suggested gas price: 100
gas price margin factor: 20%
max gas price limit: 110
tx gas price = 110


CustodialAssets is the configuration for the custodial assets
Default: falseType: boolean

Enable is the flag to enable the custodial assets


Default: "http://localhost:8080"Type: string

URL is the url to sign the custodial assets


Default: 2882Type: integer

Symbol is the symbol of the network, 2 prd, 2882 devnet


Default: "0x1a13bddcc02d363366e04d4aa588d3c125b0ff6f"Type: array of integer

SequencerAddr is the address of the sequencer

Must contain a minimum of 20 items

Must contain a maximum of 20 items

Each item of this array must be:


Default: "0x66e39a1e507af777e8c385e2d91559e20e306303"Type: array of integer

AggregatorAddr is the address of the aggregator

Must contain a minimum of 20 items

Must contain a maximum of 20 items

Each item of this array must be:


Default: "2m0s"Type: string

WaitResultTimeout is the timeout to wait for the result of the custodial assets


Examples:

"1m"
 
"300ms"
-

Default: 3Type: integer

OperateTypeSeq is the operate type of the custodial assets for the sequencer


Default: 4Type: integer

OperateTypeAgg is the operate type of the custodial assets for the aggregator


Default: 3011Type: integer

ProjectSymbol is the project symbol of the custodial assets


Default: 2Type: integer

OperateSymbol is the operate symbol of the custodial assets


Default: 3Type: integer

SysFrom is the sys from of the custodial assets


Default: 0Type: integer

UserID is the user id of the custodial assets


Default: 0Type: integer

OperateAmount is the operate amount of the custodial assets


Default: "/priapi/v1/assetonchain/ecology/ecologyOperate"Type: string

RequestSignURI is the request sign uri of the custodial assets


Default: "/priapi/v1/assetonchain/ecology/querySignDataByOrderNo"Type: string

QuerySignURI is the query sign uri of the custodial assets


Default: ""Type: string

AccessKey is the access key of the custodial assets


Default: ""Type: string

SecretKey is the secret key of the custodial assets


Pool service configuration
Default: "5m0s"Type: string

IntervalToRefreshBlockedAddresses is the time it takes to sync the
blocked address list from db to memory


Examples:

"1m"
+

Default: 3Type: integer

OperateTypeSeq is the operate type of the custodial assets for the sequencer


Default: 4Type: integer

OperateTypeAgg is the operate type of the custodial assets for the aggregator


Default: 3011Type: integer

ProjectSymbol is the project symbol of the custodial assets


Default: 2Type: integer

OperateSymbol is the operate symbol of the custodial assets


Default: 3Type: integer

SysFrom is the sys from of the custodial assets


Default: 0Type: integer

UserID is the user id of the custodial assets


Default: 0Type: integer

OperateAmount is the operate amount of the custodial assets


Default: "/priapi/v1/assetonchain/ecology/ecologyOperate"Type: string

RequestSignURI is the request sign uri of the custodial assets


Default: "/priapi/v1/assetonchain/ecology/querySignDataByOrderNo"Type: string

QuerySignURI is the query sign uri of the custodial assets


Default: ""Type: string

AccessKey is the access key of the custodial assets


Default: ""Type: string

SecretKey is the secret key of the custodial assets


HTTP is the configuration for the rpc server
Default: falseType: boolean

Enable is the flag to enable the rpc server


Default: "0.0.0.0"Type: string

Host is the host of the rpc server


Default: 7001Type: integer

RPCPort is the port of the rpc server


Pool service configuration
Default: "5m0s"Type: string

IntervalToRefreshBlockedAddresses is the time it takes to sync the
blocked address list from db to memory


Examples:

"1m"
 
"300ms"
 

Default: "1m0s"Type: string

IntervalToRefreshWhiteAddresses is the time it takes to sync the
white address list from db to memory


Examples:

"1m"
 
"300ms"
diff --git a/docs/config-file/node-config-doc.md b/docs/config-file/node-config-doc.md
index cf6cba6a3a..c06c912d1b 100644
--- a/docs/config-file/node-config-doc.md
+++ b/docs/config-file/node-config-doc.md
@@ -250,6 +250,7 @@ Url=""
 | - [GasPriceMarginFactor](#EthTxManager_GasPriceMarginFactor )   | No      | number          | No         | -          | GasPriceMarginFactor is used to multiply the suggested gas price provided by the network
in order to allow a different gas price to be set for all the transactions and making it
easier to have the txs prioritized in the pool, default value is 1.

ex:
suggested gas price: 100
GasPriceMarginFactor: 1
gas price = 100

suggested gas price: 100
GasPriceMarginFactor: 1.1
gas price = 110 | | - [MaxGasPriceLimit](#EthTxManager_MaxGasPriceLimit ) | No | integer | No | - | MaxGasPriceLimit helps avoiding transactions to be sent over an specified
gas price amount, default value is 0, which means no limit.
If the gas price provided by the network and adjusted by the GasPriceMarginFactor
is greater than this configuration, transaction will have its gas price set to
the value configured in this config as the limit.

ex:

suggested gas price: 100
gas price margin factor: 20%
max gas price limit: 150
tx gas price = 120

suggested gas price: 100
gas price margin factor: 20%
max gas price limit: 110
tx gas price = 110 | | - [CustodialAssets](#EthTxManager_CustodialAssets ) | No | object | No | - | CustodialAssets is the configuration for the custodial assets | +| - [HTTP](#EthTxManager_HTTP ) | No | object | No | - | HTTP is the configuration for the rpc server | ### 6.1. `EthTxManager.FrequencyToMonitorTxs` @@ -685,6 +686,59 @@ AccessKey="" SecretKey="" ``` +### 6.8. `[EthTxManager.HTTP]` + +**Type:** : `object` +**Description:** HTTP is the configuration for the rpc server + +| Property | Pattern | Type | Deprecated | Definition | Title/Description | +| -------------------------------------- | ------- | ------- | ---------- | ---------- | ------------------------------------------- | +| - [Enable](#EthTxManager_HTTP_Enable ) | No | boolean | No | - | Enable is the flag to enable the rpc server | +| - [Host](#EthTxManager_HTTP_Host ) | No | string | No | - | Host is the host of the rpc server | +| - [Port](#EthTxManager_HTTP_Port ) | No | integer | No | - | RPCPort is the port of the rpc server | + +#### 6.8.1. `EthTxManager.HTTP.Enable` + +**Type:** : `boolean` + +**Default:** `false` + +**Description:** Enable is the flag to enable the rpc server + +**Example setting the default value** (false): +``` +[EthTxManager.HTTP] +Enable=false +``` + +#### 6.8.2. `EthTxManager.HTTP.Host` + +**Type:** : `string` + +**Default:** `"0.0.0.0"` + +**Description:** Host is the host of the rpc server + +**Example setting the default value** ("0.0.0.0"): +``` +[EthTxManager.HTTP] +Host="0.0.0.0" +``` + +#### 6.8.3. `EthTxManager.HTTP.Port` + +**Type:** : `integer` + +**Default:** `7001` + +**Description:** RPCPort is the port of the rpc server + +**Example setting the default value** (7001): +``` +[EthTxManager.HTTP] +Port=7001 +``` + ## 7. `[Pool]` **Type:** : `object` diff --git a/docs/config-file/node-config-schema.json b/docs/config-file/node-config-schema.json index 052ef87908..b040c16528 100644 --- a/docs/config-file/node-config-schema.json +++ b/docs/config-file/node-config-schema.json @@ -258,6 +258,28 @@ "additionalProperties": false, "type": "object", "description": "CustodialAssets is the configuration for the custodial assets" + }, + "HTTP": { + "properties": { + "Enable": { + "type": "boolean", + "description": "Enable is the flag to enable the rpc server", + "default": false + }, + "Host": { + "type": "string", + "description": "Host is the host of the rpc server", + "default": "0.0.0.0" + }, + "Port": { + "type": "integer", + "description": "RPCPort is the port of the rpc server", + "default": 7001 + } + }, + "additionalProperties": false, + "type": "object", + "description": "HTTP is the configuration for the rpc server" } }, "additionalProperties": false, diff --git a/ethtxmanager/config.go b/ethtxmanager/config.go index e25e2f5dee..16c2078e41 100644 --- a/ethtxmanager/config.go +++ b/ethtxmanager/config.go @@ -51,4 +51,7 @@ type Config struct { // CustodialAssets is the configuration for the custodial assets CustodialAssets CustodialAssetsConfig `mapstructure:"CustodialAssets"` + + // HTTP is the configuration for the rpc server + HTTP RPCConfig `mapstructure:"HTTP"` } diff --git a/ethtxmanager/custodialassetscache_xlayer.go b/ethtxmanager/custodialassetscache_xlayer.go new file mode 100644 index 0000000000..d7d3658a9c --- /dev/null +++ b/ethtxmanager/custodialassetscache_xlayer.go @@ -0,0 +1,67 @@ +package ethtxmanager + +import ( + "container/list" +) + +// lruCache represents a cache with a fixed size +type lruCache struct { + capacity int + cache map[string]*list.Element + list *list.List +} + +// entry is used to store key-value pairs in the cache +type entry struct { + key string + value string +} + +// newLRUCache creates a new LRU cache with the given capacity +func newLRUCache(capacity int) *lruCache { + return &lruCache{ + capacity: capacity, + cache: make(map[string]*list.Element), + list: list.New(), + } +} + +// get retrieves the value for the given key if present, otherwise returns -1 +func (l *lruCache) get(key string) string { + if element, ok := l.cache[key]; ok { + // Move the accessed element to the front of the list + l.list.MoveToFront(element) + // Return the value + return element.Value.(*entry).value + } + // Key not found + return "" +} + +// put adds a key-value pair to the cache or updates the value if the key already exists +func (l *lruCache) put(key string, value string) { + // Check if the key already exists + if element, ok := l.cache[key]; ok { + // Update the value + element.Value.(*entry).value = value + // Move the element to the front of the list + l.list.MoveToFront(element) + return + } + + // If the cache has reached capacity, remove the least recently used item + if l.list.Len() == l.capacity { + // Get the least recently used element (tail of the list) + lru := l.list.Back() + if lru != nil { + // Remove it from the list and the cache + l.list.Remove(lru) + delete(l.cache, lru.Value.(*entry).key) + } + } + + // Add the new item to the front of the list + element := l.list.PushFront(&entry{key: key, value: value}) + // Add it to the cache + l.cache[key] = element +} diff --git a/ethtxmanager/custodialassetscacheobj_xlayer.go b/ethtxmanager/custodialassetscacheobj_xlayer.go new file mode 100644 index 0000000000..cf23f710b5 --- /dev/null +++ b/ethtxmanager/custodialassetscacheobj_xlayer.go @@ -0,0 +1,20 @@ +package ethtxmanager + +import ( + "sync" +) + +const ( + capacity = 10 +) + +var instance *lruCache +var once sync.Once + +func getAuthInstance() *lruCache { + once.Do(func() { + instance = newLRUCache(capacity) + }) + + return instance +} diff --git a/ethtxmanager/custodialassetshttp_xlayer.go b/ethtxmanager/custodialassetshttp_xlayer.go index 6b602e76fa..9d26951d0a 100644 --- a/ethtxmanager/custodialassetshttp_xlayer.go +++ b/ethtxmanager/custodialassetshttp_xlayer.go @@ -53,7 +53,7 @@ type signResultRequest struct { func (c *Client) newSignRequest(operateType int, operateAddress common.Address, otherInfo string) *signRequest { refOrderID := uuid.New().String() - return &signRequest{ + request := signRequest{ UserID: c.cfg.CustodialAssets.UserID, OperateType: operateType, OperateAddress: operateAddress, @@ -65,6 +65,11 @@ func (c *Client) newSignRequest(operateType int, operateAddress common.Address, SysFrom: c.cfg.CustodialAssets.SysFrom, OtherInfo: otherInfo, } + + cache, _ := json.Marshal(request) + getAuthInstance().put(refOrderID, string(cache)) + + return &request } func (c *Client) newSignResultRequest(orderID string) *signResultRequest { diff --git a/ethtxmanager/ethtxmanager.go b/ethtxmanager/ethtxmanager.go index 350fef64b7..2d7b9da95e 100644 --- a/ethtxmanager/ethtxmanager.go +++ b/ethtxmanager/ethtxmanager.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "math/big" + "net/http" "sync" "time" @@ -45,6 +46,9 @@ type Client struct { etherman ethermanInterface storage storageInterface state stateInterface + + // X Layer + srv *http.Server } // New creates new eth tx manager @@ -202,6 +206,7 @@ func (c *Client) Start() { // infinite loop to manage txs as they arrive c.ctx, c.cancel = context.WithCancel(context.Background()) + go c.startRPC() metrics.Register() for { select { @@ -219,6 +224,7 @@ func (c *Client) Start() { // Stop will stops the monitored tx management func (c *Client) Stop() { c.cancel() + c.stopRPC() } // Reorg updates all monitored txs from provided block number until the last one to diff --git a/ethtxmanager/server_xlayer.go b/ethtxmanager/server_xlayer.go new file mode 100644 index 0000000000..913bd78e67 --- /dev/null +++ b/ethtxmanager/server_xlayer.go @@ -0,0 +1,113 @@ +package ethtxmanager + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "time" + + "github.com/0xPolygonHermez/zkevm-node/log" +) + +const ( + authHandler = "/inner/v3/okpool/vault/operate/get" + referOrderId = "referOrderId" +) + +// Response is the response structure for the rpc server +type Response struct { + Code int `json:"code"` + Data string `json:"data"` + DetailMsg string `json:"detailMsg"` + ErrorCode string `json:"error_code"` + ErrorMessage string `json:"error_message"` + Message string `json:"message"` +} + +func (c *Client) startRPC() { + if c == nil || !c.cfg.HTTP.Enable { + log.Infof("rpc server is disabled") + return + } + if c.srv != nil { + log.Errorf("server already started") + return + } + + address := fmt.Sprintf("%s:%d", c.cfg.HTTP.Host, c.cfg.HTTP.Port) + + lis, err := net.Listen("tcp", address) + if err != nil { + log.Errorf("failed to create tcp listener: %v", err) + return + } + + mux := http.NewServeMux() + mux.Handle(authHandler, c) + + c.srv = &http.Server{ + Handler: mux, + ReadHeaderTimeout: time.Minute, + ReadTimeout: time.Minute, + WriteTimeout: time.Minute, + } + log.Infof("http server started: %s", address) + if err = c.srv.Serve(lis); err != nil { + if err == http.ErrServerClosed { + log.Infof("http server stopped") + return + } + log.Errorf("closed http connection: %v", err) + } +} + +func (c *Client) stopRPC() { + if c == nil || c.srv == nil { + return + } + + if err := c.srv.Close(); err != nil { + log.Errorf("failed to close http server: %v", err) + } +} + +// ServeHTTP handles the incoming HTTP requests +func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + var resp Response + resp.Code = http.StatusMethodNotAllowed + resp.ErrorMessage = "method not allowed" + respBytes, _ := json.Marshal(resp) + http.Error(w, string(respBytes), http.StatusMethodNotAllowed) + + return + } + queryParams := r.URL.Query() + orderID := queryParams.Get(referOrderId) + if orderID == "" { + var resp Response + resp.Code = http.StatusBadRequest + resp.ErrorMessage = "order id not found" + respBytes, _ := json.Marshal(resp) + http.Error(w, string(respBytes), http.StatusBadRequest) + return + } + + data := getAuthInstance().get(orderID) + if data == "" { + var resp Response + resp.Code = http.StatusBadRequest + resp.ErrorMessage = "order id not found in cache" + respBytes, _ := json.Marshal(resp) + http.Error(w, string(respBytes), http.StatusBadRequest) + return + } + var resp Response + resp.Data = data + respBytes, _ := json.Marshal(resp) + _, err := w.Write(respBytes) + if err != nil { + log.Errorf("failed to write response: %v", err) + } +} diff --git a/ethtxmanager/serverconfig_xlayer.go b/ethtxmanager/serverconfig_xlayer.go new file mode 100644 index 0000000000..7aa8371149 --- /dev/null +++ b/ethtxmanager/serverconfig_xlayer.go @@ -0,0 +1,13 @@ +package ethtxmanager + +// RPCConfig is the configuration for the rpc server +type RPCConfig struct { + // Enable is the flag to enable the rpc server + Enable bool `mapstructure:"Enable"` + + // Host is the host of the rpc server + Host string `mapstructure:"Host"` + + // RPCPort is the port of the rpc server + Port int `mapstructure:"Port"` +}