Skip to content

Commit

Permalink
ocr3 - performance optimize msghasher (#1115)
Browse files Browse the repository at this point in the history
Performance optimizations of msgHasher, ~4 times faster by parsing ABIs
once.
  • Loading branch information
dimkouv authored Jun 28, 2024
1 parent ecdd5c2 commit 8cf4caa
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 29 deletions.
73 changes: 45 additions & 28 deletions core/services/ocr3/plugins/ccipevm/msghasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,42 @@ import (
type MessageHasherV1 struct {
metaDataHash [32]byte
leafDomainSeparator [32]byte

// ABIs and types for encoding the message data similar to on-chain implementation:
// https://github.com/smartcontractkit/ccip/blob/54ee4f13143d3e414627b6a0b9f71d5dfade76c5/contracts/src/v0.8/ccip/libraries/Internal.sol#L135
bytesArrayType abi.Type
tokensAbi abi.ABI
fixedSizeValuesAbi abi.ABI
packedValuesAbi abi.ABI
}

func NewMessageHasherV1(metaDataHash [32]byte) *MessageHasherV1 {
bytesArray, err := abi.NewType("bytes[]", "bytes[]", nil)
if err != nil {
panic(fmt.Sprintf("failed to create bytes[] type: %v", err))
}

return &MessageHasherV1{
metaDataHash: metaDataHash,
leafDomainSeparator: [32]byte{},

bytesArrayType: bytesArray,
tokensAbi: mustParseInputsAbi(`[{"components": [{"name":"token","type":"address"},
{"name":"amount","type":"uint256"}], "type":"tuple[]"}]`),
fixedSizeValuesAbi: mustParseInputsAbi(`[{"name": "sender", "type":"address"},
{"name": "receiver", "type":"address"},
{"name": "sequenceNumber", "type":"uint64"},
{"name": "gasLimit", "type":"uint256"},
{"name": "strict", "type":"bool"},
{"name": "nonce", "type":"uint64"},
{"name": "feeToken","type": "address"},
{"name": "feeTokenAmount","type": "uint256"}]`),
packedValuesAbi: mustParseInputsAbi(`[{"name": "leafDomainSeparator","type":"bytes32"},
{"name": "metadataHash", "type":"bytes32"},
{"name": "fixedSizeValuesHash", "type":"bytes32"},
{"name": "dataHash", "type":"bytes32"},
{"name": "tokenAmountsHash", "type":"bytes32"},
{"name": "sourceTokenDataHash", "type":"bytes32"}]`),
}
}

Expand All @@ -41,31 +71,19 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (ccipty
Amount: ta.Amount,
}
}
encodedTokens, err := h.abiEncode(`[{"components": [{"name":"token","type":"address"},
{"name":"amount","type":"uint256"}], "type":"tuple[]"}]`, tokenAmounts)
encodedTokens, err := h.abiEncode(h.tokensAbi, tokenAmounts)
if err != nil {
return [32]byte{}, fmt.Errorf("abi encode token amounts: %w", err)
}

bytesArray, err := abi.NewType("bytes[]", "bytes[]", nil)
if err != nil {
return [32]byte{}, fmt.Errorf("new abi type bytes[]: %w", err)
}

encodedSourceTokenData, err := abi.Arguments{abi.Argument{Type: bytesArray}}.PackValues([]interface{}{msg.SourceTokenData})
encodedSourceTokenData, err := abi.Arguments{abi.Argument{Type: h.bytesArrayType}}.
PackValues([]interface{}{msg.SourceTokenData})
if err != nil {
return [32]byte{}, fmt.Errorf("pack source token data: %w", err)
}

packedFixedSizeValues, err := h.abiEncode(
`[{"name": "sender", "type":"address"},
{"name": "receiver", "type":"address"},
{"name": "sequenceNumber", "type":"uint64"},
{"name": "gasLimit", "type":"uint256"},
{"name": "strict", "type":"bool"},
{"name": "nonce", "type":"uint64"},
{"name": "feeToken","type": "address"},
{"name": "feeTokenAmount","type": "uint256"}]`,
h.fixedSizeValuesAbi,
common.HexToAddress(string(msg.Sender)),
common.HexToAddress(string(msg.Receiver)),
uint64(msg.SeqNum),
Expand All @@ -81,12 +99,7 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (ccipty
fixedSizeValuesHash := utils.Keccak256Fixed(packedFixedSizeValues)

packedValues, err := h.abiEncode(
`[{"name": "leafDomainSeparator","type":"bytes32"},
{"name": "metadataHash", "type":"bytes32"},
{"name": "fixedSizeValuesHash", "type":"bytes32"},
{"name": "dataHash", "type":"bytes32"},
{"name": "tokenAmountsHash", "type":"bytes32"},
{"name": "sourceTokenDataHash", "type":"bytes32"}]`,
h.packedValuesAbi,
h.leafDomainSeparator,
h.metaDataHash,
fixedSizeValuesHash,
Expand All @@ -101,17 +114,21 @@ func (h *MessageHasherV1) Hash(_ context.Context, msg cciptypes.CCIPMsg) (ccipty
return utils.Keccak256Fixed(packedValues), nil
}

func (h *MessageHasherV1) abiEncode(abiStr string, values ...interface{}) ([]byte, error) {
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, abiStr)
inAbi, err := abi.JSON(strings.NewReader(inDef))
func (h *MessageHasherV1) abiEncode(theAbi abi.ABI, values ...interface{}) ([]byte, error) {
res, err := theAbi.Pack("method", values...)
if err != nil {
return nil, err
}
res, err := inAbi.Pack("method", values...)
return res[4:], nil
}

func mustParseInputsAbi(s string) abi.ABI {
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, s)
inAbi, err := abi.JSON(strings.NewReader(inDef))
if err != nil {
return nil, err
panic(fmt.Errorf("failed to create %s ABI: %v", s, err))
}
return res[4:], nil
return inAbi
}

// Interface compliance check
Expand Down
35 changes: 34 additions & 1 deletion core/services/ocr3/plugins/ccipevm/msghasher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func testSetup(t *testing.T) *testSetupData {
func TestMessageHasher_Hash(t *testing.T) {
ctx := testutils.Context(t)

largeNumber, ok := big.NewInt(0).SetString("1000000000000000000", 10) //1e18
largeNumber, ok := big.NewInt(0).SetString("1000000000000000000", 10) // 1e18
require.True(t, ok)

msgData, err := hex.DecodeString("64617461")
Expand Down Expand Up @@ -294,3 +294,36 @@ func TestMessageHasher_Hash(t *testing.T) {
})
}
}

func BenchmarkMessageHasher_Hash(b *testing.B) {
ctx := testutils.Context(b)
msg := cciptypes.CCIPMsg{
CCIPMsgBaseDetails: cciptypes.CCIPMsgBaseDetails{
SourceChain: 1,
SeqNum: 1,
},
ChainFeeLimit: cciptypes.NewBigIntFromInt64(400000),
Nonce: 1,
Sender: types.Account(utils.RandomAddress().String()),
Receiver: types.Account(utils.RandomAddress().String()),
Strict: false,
FeeToken: types.Account(utils.RandomAddress().String()),
FeeTokenAmount: cciptypes.NewBigIntFromInt64(1234567890),
Data: make([]byte, 2048),
TokenAmounts: []cciptypes.TokenAmount{
{
Token: types.Account(utils.RandomAddress().String()),
Amount: utils.RandUint256(),
},
},
SourceTokenData: [][]byte{make([]byte, 2048)},
}
metadataHash := utils.RandomBytes32()

m := NewMessageHasherV1(metadataHash)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := m.Hash(ctx, msg)
require.NoError(b, err)
}
}

0 comments on commit 8cf4caa

Please sign in to comment.