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

BCFR-1099 sei custom log index #15858

Merged
merged 7 commits into from
Jan 7, 2025
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
5 changes: 5 additions & 0 deletions .changeset/clever-knives-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink": patch
---

#added Sei config and error mapping
18 changes: 18 additions & 0 deletions ccip/config/evm/Sei_Testnet_Atlantic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
ChainID = '1328'
ChainType = 'sei'
# finality_depth: instant
FinalityDepth = 10
# block_time: ~0.4s, adding 1 second buffer
LogPollInterval = '2s'
# finality_depth * block_time / 60 secs = ~0.8 min (finality time)
NoNewFinalizedHeadsThreshold = '5m'
# "RPC node returned multiple missing blocks on query for block numbers [31592085 31592084] even though the WS subscription already sent us these blocks. It might help to increase EVM.RPCBlockQueryDelay (currently 1)"
RPCBlockQueryDelay = 5

[GasEstimator]
EIP1559DynamicFees = false
Mode = 'BlockHistory'
PriceMax = '3000 gwei' # recommended by ds&a

[GasEstimator.BlockHistory]
BlockHistorySize = 200
3 changes: 3 additions & 0 deletions core/build/platform_arch_guard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//go:build !amd64 && !arm64
package build
"non-64-bits architectures are not supported"
12 changes: 11 additions & 1 deletion core/chains/evm/client/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,24 @@ var gnosis = ClientErrors{
TransactionAlreadyInMempool: regexp.MustCompile(`(: |^)(alreadyknown)`),
}

var sei = ClientErrors{
// https://github.com/sei-protocol/sei-tendermint/blob/e9a22c961e83579d8a68cd045c532980d82fb2a0/types/mempool.go#L12
TransactionAlreadyInMempool: regexp.MustCompile("tx already exists in cache"),
// https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/types/errors/errors.go#L50
// https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/types/errors/errors.go#L56
// https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/client/broadcast.go#L27
// https://github.com/sei-protocol/sei-cosmos/blob/a4eb451c957b1ca7ca9118406682f93fe83d1f61/types/errors/errors.go#L32
Fatal: regexp.MustCompile(`(: |^)'*out of gas|insufficient fee|Tx too large. Max size is \d+, but got \d+|: insufficient funds`),
}

const TerminallyStuckMsg = "transaction terminally stuck"

// Tx.Error messages that are set internally so they are not chain or client specific
var internal = ClientErrors{
TerminallyStuck: regexp.MustCompile(TerminallyStuckMsg),
}

var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo, zkSync, zkEvm, treasure, mantle, aStar, hedera, gnosis, internal}
var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo, zkSync, zkEvm, treasure, mantle, aStar, hedera, gnosis, sei, internal}

// ClientErrorRegexes returns a map of compiled regexes for each error type
func ClientErrorRegexes(errsRegex config.ClientErrors) *ClientErrors {
Expand Down
6 changes: 6 additions & 0 deletions core/chains/evm/client/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func Test_Eth_Errors(t *testing.T) {
{"ErrorObject { code: ServerError(3), message: \\\"known transaction. transaction with hash 0xf016…ad63 is already in the system\\\", data: Some(RawValue(\\\"0x\\\")) }", true, "zkSync"},
{"client error transaction already in mempool", true, "tomlConfig"},
{"alreadyknown", true, "Gnosis"},
{"tx already exists in cache", true, "Sei"},
}
for _, test := range tests {
err = evmclient.NewSendErrorS(test.message)
Expand Down Expand Up @@ -442,6 +443,11 @@ func Test_Eth_Errors_Fatal(t *testing.T) {
{"client error fatal", true, "tomlConfig"},
{"[Request ID: d9711488-4c1e-4af2-bc1f-7969913d7b60] Error invoking RPC: transaction [email protected] failed precheck with status INVALID_SIGNATURE", true, "hedera"},
{"invalid chain id for signer", true, "Treasure"},

{": out of gas", true, "Sei"},
{"Tx too large. Max size is 2048576, but got 2097431", true, "Sei"},
{": insufficient funds", true, "Sei"},
{"insufficient fee", true, "Sei"},
}

for _, test := range tests {
Expand Down
8 changes: 6 additions & 2 deletions core/chains/evm/client/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"net/url"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -216,6 +217,7 @@ const HeadResult = `{"difficulty":"0xf3a00","extraData":"0xd88301050384676574688
type mockSubscription struct {
unsubscribed bool
Errors chan error
unsub sync.Once
}

func NewMockSubscription() *mockSubscription {
Expand All @@ -225,8 +227,10 @@ func NewMockSubscription() *mockSubscription {
func (mes *mockSubscription) Err() <-chan error { return mes.Errors }

func (mes *mockSubscription) Unsubscribe() {
mes.unsubscribed = true
close(mes.Errors)
mes.unsub.Do(func() {
mes.unsubscribed = true
close(mes.Errors)
})
}

func ParseTestNodeConfigs(nodes []NodeConfig) ([]*toml.Node, error) {
Expand Down
51 changes: 47 additions & 4 deletions core/chains/evm/client/rpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"math/big"
"net/url"
"strconv"
Expand Down Expand Up @@ -376,6 +377,10 @@ func (r *RPCClient) BatchCallContext(rootCtx context.Context, b []rpc.BatchElem)
var requestedFinalizedBlock bool
if r.chainType == chaintype.ChainAstar {
for _, el := range b {
if el.Method == "eth_getLogs" {
r.rpcLog.Critical("evmclient.BatchCallContext: eth_getLogs is not supported")
return errors.New("evmclient.BatchCallContext: eth_getLogs is not supported")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean we can't support LogPoller / CCIP on AStar?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, nevermind... I forgot we only use batch requests with eth_getBlockByNumber. Still, very strange that AStar wouldn't support this! We have thought about changing the eth_getLogs requests to a batched request, I guess if we do that we'll have to fall back to the older method on AStar 🤔

if !isRequestingFinalizedBlock(el) {
continue
}
Expand Down Expand Up @@ -490,10 +495,10 @@ func (r *RPCClient) SubscribeToHeads(ctx context.Context) (ch <-chan *evmtypes.H
}()

channel := make(chan *evmtypes.Head)
forwarder := newSubForwarder(channel, func(head *evmtypes.Head) *evmtypes.Head {
forwarder := newSubForwarder(channel, func(head *evmtypes.Head) (*evmtypes.Head, error) {
head.EVMChainID = ubig.New(r.chainID)
r.onNewHead(ctx, chStopInFlight, head)
return head
return head, nil
}, r.wrapRPCClientError)

err = forwarder.start(ws.rpc.EthSubscribe(ctx, forwarder.srcCh, args...))
Expand Down Expand Up @@ -1199,8 +1204,11 @@ func (r *RPCClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (l [
l, err = ws.geth.FilterLogs(ctx, q)
err = r.wrapWS(err)
}
duration := time.Since(start)

if err == nil {
err = r.makeLogsValid(l)
}
duration := time.Since(start)
r.logResult(lggr, err, duration, r.getRPCDomain(), "FilterLogs",
"log", l,
)
Expand Down Expand Up @@ -1228,7 +1236,7 @@ func (r *RPCClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQu
r.logResult(lggr, err, duration, r.getRPCDomain(), "SubscribeFilterLogs")
err = r.wrapWS(err)
}()
sub := newSubForwarder(ch, nil, r.wrapRPCClientError)
sub := newSubForwarder(ch, r.makeLogValid, r.wrapRPCClientError)
err = sub.start(ws.geth.SubscribeFilterLogs(ctx, q, sub.srcCh))
if err != nil {
return
Expand Down Expand Up @@ -1452,3 +1460,38 @@ func ToBlockNumArg(number *big.Int) string {
}
return hexutil.EncodeBig(number)
}

func (r *RPCClient) makeLogsValid(logs []types.Log) error {
if r.chainType != chaintype.ChainSei {
return nil
}

for i := range logs {
var err error
logs[i], err = r.makeLogValid(logs[i])
if err != nil {
return err
}
}

return nil
}

func (r *RPCClient) makeLogValid(log types.Log) (types.Log, error) {
if r.chainType != chaintype.ChainSei {
return log, nil
}

if log.TxIndex > math.MaxUint32 {
return types.Log{}, fmt.Errorf("TxIndex of tx %s exceeds max supported value of %d", log.TxHash, math.MaxUint32)
}

if log.Index > math.MaxUint32 {
return types.Log{}, fmt.Errorf("log's index %d of tx %s exceeds max supported value of %d", log.Index, log.TxHash, math.MaxUint32)
}

// it's safe as we have a build guard to guarantee 64-bit system
newIndex := uint64(log.TxIndex<<32) | uint64(log.Index)
log.Index = uint(newIndex)
return log, nil
}
93 changes: 93 additions & 0 deletions core/chains/evm/client/rpc_client_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package client

import (
"errors"
"math"
"testing"

ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require"

commonclient "github.com/smartcontractkit/chainlink/v2/common/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/config/chaintype"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

func TestRPCClient_MakeLogsValid(t *testing.T) {
testCases := []struct {
Name string
TxIndex uint
LogIndex uint
ExpectedLogIndex uint
ExpectedError error
}{
{
Name: "TxIndex = 0 LogIndex = 0",
TxIndex: 0,
LogIndex: 0,
ExpectedLogIndex: 0,
ExpectedError: nil,
},
{
Name: "TxIndex = 0 LogIndex = 1",
TxIndex: 0,
LogIndex: 1,
ExpectedLogIndex: 1,
ExpectedError: nil,
},
{
Name: "TxIndex = 0 LogIndex = MaxUint32",
TxIndex: 0,
LogIndex: math.MaxUint32,
ExpectedLogIndex: math.MaxUint32,
ExpectedError: nil,
},
{
Name: "LogIndex = MaxUint32 + 1 => returns an error",
TxIndex: 0,
LogIndex: math.MaxUint32 + 1,
ExpectedLogIndex: 0,
ExpectedError: errors.New("log's index 4294967296 of tx 0x0000000000000000000000000000000000000000000000000000000000000000 exceeds max supported value of 4294967295"),
},
{
Name: "TxIndex = 1 LogIndex = 0",
TxIndex: 1,
LogIndex: 0,
ExpectedLogIndex: math.MaxUint32 + 1,
ExpectedError: nil,
},
{
Name: "TxIndex = MaxUint32 LogIndex = MaxUint32",
TxIndex: math.MaxUint32,
LogIndex: math.MaxUint32,
ExpectedLogIndex: math.MaxUint64,
ExpectedError: nil,
},
{
Name: "TxIndex = MaxUint32 + 1 => returns an error",
TxIndex: math.MaxUint32 + 1,
LogIndex: 0,
ExpectedLogIndex: 0,
ExpectedError: errors.New("TxIndex of tx 0x0000000000000000000000000000000000000000000000000000000000000000 exceeds max supported value of 4294967295"),
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
rpc := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, "")
log, err := rpc.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex})
// non sei should return as is
require.NoError(t, err)
require.Equal(t, tc.TxIndex, log.TxIndex)
require.Equal(t, tc.LogIndex, log.Index)
seiRPC := NewRPCClient(TestNodePoolConfig{}, logger.TestLogger(t), nil, nil, "eth-primary-rpc-0", 0, nil, commonclient.Primary, commonclient.QueryTimeout, commonclient.QueryTimeout, chaintype.ChainSei)
log, err = seiRPC.makeLogValid(ethtypes.Log{TxIndex: tc.TxIndex, Index: tc.LogIndex})
if tc.ExpectedError != nil {
require.EqualError(t, err, tc.ExpectedError.Error())
return
}

require.Equal(t, tc.ExpectedLogIndex, log.Index)
require.Equal(t, tc.TxIndex, log.TxIndex)
})
}
}
Loading
Loading