-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'ccip-develop' into origin/CCIP-2599-open-zeppelin-v-5-m…
…igration-2
- Loading branch information
Showing
12 changed files
with
284 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package ccipevm | ||
|
||
import ( | ||
"math" | ||
|
||
cciptypes "github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" | ||
) | ||
|
||
const ( | ||
EvmAddressLengthBytes = 20 | ||
EvmWordBytes = 32 | ||
CalldataGasPerByte = 16 | ||
TokenAdminRegistryWarmupCost = 2_500 | ||
TokenAdminRegistryPoolLookupGas = 100 + // WARM_ACCESS_COST TokenAdminRegistry | ||
700 + // CALL cost for TokenAdminRegistry | ||
2_100 // COLD_SLOAD_COST loading the pool address | ||
SupportsInterfaceCheck = 2600 + // because the receiver will be untouched initially | ||
30_000*3 // supportsInterface of ERC165Checker library performs 3 static-calls of 30k gas each | ||
PerTokenOverheadGas = TokenAdminRegistryPoolLookupGas + | ||
SupportsInterfaceCheck + | ||
200_000 + // releaseOrMint using callWithExactGas | ||
50_000 // transfer using callWithExactGas | ||
RateLimiterOverheadGas = 2_100 + // COLD_SLOAD_COST for accessing token bucket | ||
5_000 // SSTORE_RESET_GAS for updating & decreasing token bucket | ||
ConstantMessagePartBytes = 10 * 32 // A message consists of 10 abi encoded fields 32B each (after encoding) | ||
ExecutionStateProcessingOverheadGas = 2_100 + // COLD_SLOAD_COST for first reading the state | ||
20_000 + // SSTORE_SET_GAS for writing from 0 (untouched) to non-zero (in-progress) | ||
100 //# SLOAD_GAS = WARM_STORAGE_READ_COST for rewriting from non-zero (in-progress) to non-zero (success/failure) | ||
) | ||
|
||
func NewGasEstimateProvider() EstimateProvider { | ||
return EstimateProvider{} | ||
} | ||
|
||
type EstimateProvider struct { | ||
} | ||
|
||
// CalculateMerkleTreeGas estimates the merkle tree gas based on number of requests | ||
func (gp EstimateProvider) CalculateMerkleTreeGas(numRequests int) uint64 { | ||
if numRequests == 0 { | ||
return 0 | ||
} | ||
merkleProofBytes := (math.Ceil(math.Log2(float64(numRequests))))*32 + (1+2)*32 // only ever one outer root hash | ||
return uint64(merkleProofBytes * CalldataGasPerByte) | ||
} | ||
|
||
// return the size of bytes for msg tokens | ||
func bytesForMsgTokens(numTokens int) int { | ||
// token address (address) + token amount (uint256) | ||
return (EvmAddressLengthBytes + EvmWordBytes) * numTokens | ||
} | ||
|
||
// CalculateMessageMaxGas computes the maximum gas overhead for a message. | ||
func (gp EstimateProvider) CalculateMessageMaxGas(msg cciptypes.Message) uint64 { | ||
numTokens := len(msg.TokenAmounts) | ||
var data []byte = msg.Data | ||
dataLength := len(data) | ||
|
||
// TODO: update interface to return error? | ||
// Although this decoding should never fail. | ||
messageGasLimit, err := decodeExtraArgsV1V2(msg.ExtraArgs) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
messageBytes := ConstantMessagePartBytes + | ||
bytesForMsgTokens(numTokens) + | ||
dataLength | ||
|
||
messageCallDataGas := uint64(messageBytes * CalldataGasPerByte) | ||
|
||
// Rate limiter only limits value in tokens. It's not called if there are no | ||
// tokens in the message. The same goes for the admin registry, it's only loaded | ||
// if there are tokens, and it's only loaded once. | ||
rateLimiterOverhead := uint64(0) | ||
adminRegistryOverhead := uint64(0) | ||
if numTokens >= 1 { | ||
rateLimiterOverhead = RateLimiterOverheadGas | ||
adminRegistryOverhead = TokenAdminRegistryWarmupCost | ||
} | ||
|
||
return messageGasLimit.Uint64() + | ||
messageCallDataGas + | ||
ExecutionStateProcessingOverheadGas + | ||
SupportsInterfaceCheck + | ||
adminRegistryOverhead + | ||
rateLimiterOverhead + | ||
PerTokenOverheadGas*uint64(numTokens) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package ccipevm | ||
|
||
import ( | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" | ||
) | ||
|
||
func Test_calculateMessageMaxGas(t *testing.T) { | ||
type args struct { | ||
dataLen int | ||
numTokens int | ||
extraArgs []byte | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want uint64 | ||
}{ | ||
{ | ||
name: "base", | ||
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV1(200_000)}, | ||
want: 1_022_264, | ||
}, | ||
{ | ||
name: "large", | ||
args: args{dataLen: 1000, numTokens: 1000, extraArgs: makeExtraArgsV1(200_000)}, | ||
want: 346_677_520, | ||
}, | ||
{ | ||
name: "overheadGas test 1", | ||
args: args{dataLen: 0, numTokens: 0, extraArgs: makeExtraArgsV1(200_000)}, | ||
want: 319_920, | ||
}, | ||
{ | ||
name: "overheadGas test 2", | ||
args: args{dataLen: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), numTokens: 1, extraArgs: makeExtraArgsV1(200_000)}, | ||
want: 675_948, | ||
}, | ||
{ | ||
name: "allowOOO set to true makes no difference to final gas estimate", | ||
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV2(200_000, true)}, | ||
want: 1_022_264, | ||
}, | ||
{ | ||
name: "allowOOO set to false makes no difference to final gas estimate", | ||
args: args{dataLen: 5, numTokens: 2, extraArgs: makeExtraArgsV2(200_000, false)}, | ||
want: 1_022_264, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
msg := ccipocr3.Message{ | ||
Data: make([]byte, tt.args.dataLen), | ||
TokenAmounts: make([]ccipocr3.RampTokenAmount, tt.args.numTokens), | ||
ExtraArgs: tt.args.extraArgs, | ||
} | ||
ep := EstimateProvider{} | ||
got := ep.CalculateMessageMaxGas(msg) | ||
t.Log(got) | ||
assert.Equalf(t, tt.want, got, "calculateMessageMaxGas(%v, %v)", tt.args.dataLen, tt.args.numTokens) | ||
}) | ||
} | ||
} | ||
|
||
// TestCalculateMaxGas is taken from the ccip repo where the CalculateMerkleTreeGas and CalculateMessageMaxGas values | ||
// are combined to one function. | ||
func TestCalculateMaxGas(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
numRequests int | ||
dataLength int | ||
numberOfTokens int | ||
extraArgs []byte | ||
want uint64 | ||
}{ | ||
{ | ||
name: "maxGasOverheadGas 1", | ||
numRequests: 6, | ||
dataLength: 0, | ||
numberOfTokens: 0, | ||
extraArgs: makeExtraArgsV1(200_000), | ||
want: 322_992, | ||
}, | ||
{ | ||
name: "maxGasOverheadGas 2", | ||
numRequests: 3, | ||
dataLength: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), | ||
numberOfTokens: 1, | ||
extraArgs: makeExtraArgsV1(200_000), | ||
want: 678_508, | ||
}, | ||
{ | ||
name: "v2 extra args", | ||
numRequests: 3, | ||
dataLength: len([]byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), | ||
numberOfTokens: 1, | ||
extraArgs: makeExtraArgsV2(200_000, true), | ||
want: 678_508, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
msg := ccipocr3.Message{ | ||
Data: make([]byte, tt.dataLength), | ||
TokenAmounts: make([]ccipocr3.RampTokenAmount, tt.numberOfTokens), | ||
ExtraArgs: tt.extraArgs, | ||
} | ||
ep := EstimateProvider{} | ||
|
||
gotTree := ep.CalculateMerkleTreeGas(tt.numRequests) | ||
gotMsg := ep.CalculateMessageMaxGas(msg) | ||
t.Log("want", tt.want, "got", gotTree+gotMsg) | ||
assert.Equal(t, tt.want, gotTree+gotMsg) | ||
}) | ||
} | ||
} | ||
|
||
func makeExtraArgsV1(gasLimit uint64) []byte { | ||
// extra args is the tag followed by the gas limit abi-encoded. | ||
var extraArgs []byte | ||
extraArgs = append(extraArgs, evmExtraArgsV1Tag...) | ||
gasLimitBytes := new(big.Int).SetUint64(gasLimit).Bytes() | ||
// pad from the left to 32 bytes | ||
gasLimitBytes = common.LeftPadBytes(gasLimitBytes, 32) | ||
extraArgs = append(extraArgs, gasLimitBytes...) | ||
return extraArgs | ||
} | ||
|
||
func makeExtraArgsV2(gasLimit uint64, allowOOO bool) []byte { | ||
// extra args is the tag followed by the gas limit and allowOOO abi-encoded. | ||
var extraArgs []byte | ||
extraArgs = append(extraArgs, evmExtraArgsV2Tag...) | ||
gasLimitBytes := new(big.Int).SetUint64(gasLimit).Bytes() | ||
// pad from the left to 32 bytes | ||
gasLimitBytes = common.LeftPadBytes(gasLimitBytes, 32) | ||
|
||
// abi-encode allowOOO | ||
var allowOOOBytes []byte | ||
if allowOOO { | ||
allowOOOBytes = append(allowOOOBytes, 1) | ||
} else { | ||
allowOOOBytes = append(allowOOOBytes, 0) | ||
} | ||
// pad from the left to 32 bytes | ||
allowOOOBytes = common.LeftPadBytes(allowOOOBytes, 32) | ||
|
||
extraArgs = append(extraArgs, gasLimitBytes...) | ||
extraArgs = append(extraArgs, allowOOOBytes...) | ||
return extraArgs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package superfakes | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/smartcontractkit/chainlink-ccip/execute/exectypes" | ||
"github.com/smartcontractkit/chainlink-common/pkg/types/ccipocr3" | ||
) | ||
|
||
// NewNilTokenDataReader returns a new nilTokenDataReader. | ||
// This token data reader always returns nil for the token data. | ||
func NewNilTokenDataReader() exectypes.TokenDataReader { | ||
return &nilTokenDataReader{} | ||
} | ||
|
||
type nilTokenDataReader struct{} | ||
|
||
// ReadTokenData implements exectypes.TokenDataReader. | ||
func (t *nilTokenDataReader) ReadTokenData(ctx context.Context, srcChain ccipocr3.ChainSelector, num ccipocr3.SeqNum) (r [][]byte, err error) { | ||
return nil, nil | ||
} | ||
|
||
var _ exectypes.TokenDataReader = (*nilTokenDataReader)(nil) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters