From 8b2c48dbcdd31ed5defff930f66b8cc7a49b9817 Mon Sep 17 00:00:00 2001 From: Lei Date: Wed, 13 Dec 2023 14:42:33 -0800 Subject: [PATCH] Mercury Packer (#11521) * Refactor encoding.Packer to have Mercury control its own interface and function implementations * rebase and update --- core/scripts/chaincli/handler/debug.go | 37 ++-- .../evmregistry/v21/encoding/interface.go | 7 - .../evmregistry/v21/encoding/packer.go | 48 ----- .../evmregistry/v21/encoding/packer_test.go | 186 ----------------- .../evmregistry/v21/mercury/mercury.go | 75 ++++++- .../evmregistry/v21/mercury/mercury_test.go | 193 ++++++++++++++++++ .../v21/mercury/streams/streams.go | 6 +- .../v21/mercury/streams/streams_test.go | 2 - .../ocr2keeper/evmregistry/v21/registry.go | 3 +- 9 files changed, 290 insertions(+), 267 deletions(-) diff --git a/core/scripts/chaincli/handler/debug.go b/core/scripts/chaincli/handler/debug.go index 815a0c9a030..3e8bd6d597e 100644 --- a/core/scripts/chaincli/handler/debug.go +++ b/core/scripts/chaincli/handler/debug.go @@ -23,7 +23,6 @@ import ( ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" evm21 "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/core/scripts/chaincli/config" "github.com/smartcontractkit/chainlink/core/scripts/common" @@ -33,6 +32,7 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams" "github.com/smartcontractkit/chainlink/v2/core/utils" bigmath "github.com/smartcontractkit/chainlink/v2/core/utils/big_math" @@ -44,6 +44,7 @@ const ( expectedTypeAndVersion = "KeeperRegistry 2.1.0" ) +var mercuryPacker = mercury.NewAbiPacker() var packer = encoding.NewAbiPacker() var links []string @@ -258,10 +259,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { mercuryConfig := evm21.NewMercuryConfig(mc, core.StreamsCompatibleABI) lggr, _ := logger.NewLogger() blockSub := &blockSubscriber{k.client} - streams := streams.NewStreamsLookup(packer, mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) + streams := streams.NewStreamsLookup(mercuryConfig, blockSub, k.rpcClient, keeperRegistry21, lggr) var streamsLookupErr *mercury.StreamsLookupError - streamsLookupErr, err = packer.DecodeStreamsLookupRequest(checkResult.PerformData) + streamsLookupErr, err = mercuryPacker.DecodeStreamsLookupRequest(checkResult.PerformData) if err == nil { message("upkeep reverted with StreamsLookup") message(fmt.Sprintf("StreamsLookup data: {FeedParamKey: %s, Feeds: %v, TimeParamKey: %s, Time: %d, ExtraData: %s}", streamsLookupErr.FeedParamKey, streamsLookupErr.Feeds, streamsLookupErr.TimeParamKey, streamsLookupErr.Time.Uint64(), hexutil.Encode(streamsLookupErr.ExtraData))) @@ -282,7 +283,7 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { message("using mercury lookup v0.2") // check if upkeep is allowed to use mercury v0.2 var allowed bool - _, _, _, allowed, err = streams.AllowedToUseMercury(latestCallOpts, upkeepID) + _, _, _, allowed, err = streams.AllowedToUseMercury(triggerCallOpts, upkeepID) if err != nil { failUnknown("failed to check if upkeep is allowed to use mercury", err) } @@ -307,10 +308,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { var values [][]byte values, err = streams.DoMercuryRequest(ctx, streamsLookup, checkResults, 0) - if automationCheckResult.IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { + if checkResults[0].IneligibilityReason == uint8(mercury.MercuryUpkeepFailureReasonInvalidRevertDataInput) { resolveIneligible("upkeep used invalid revert data") } - if automationCheckResult.PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { + if checkResults[0].PipelineExecutionState == uint8(mercury.InvalidMercuryRequest) { resolveIneligible("the mercury request data is invalid") } if err != nil { @@ -322,10 +323,10 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if err != nil { failUnknown("failed to execute mercury callback ", err) } - if automationCheckResult.IneligibilityReason != 0 { - message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", automationCheckResult.IneligibilityReason)) + if checkResults[0].IneligibilityReason != 0 { + message(fmt.Sprintf("checkCallback failed with UpkeepFailureReason %d", checkResults[0].IneligibilityReason)) } - upkeepNeeded, performData = automationCheckResult.Eligible, automationCheckResult.PerformData + upkeepNeeded, performData = checkResults[0].Eligible, checkResults[0].PerformData // do tenderly simulations for checkCallback var rawCall []byte rawCall, err = core.RegistryABI.Pack("checkCallback", upkeepID, values, streamsLookup.ExtraData) @@ -345,13 +346,23 @@ func (k *Keeper) Debug(ctx context.Context, args []string) { if !upkeepNeeded { resolveIneligible("upkeep is not needed") } - // simulate perform ukeep - simulateResult, err := keeperRegistry21.SimulatePerformUpkeep(latestCallOpts, upkeepID, performData) + // simulate perform upkeep + simulateResult, err := keeperRegistry21.SimulatePerformUpkeep(triggerCallOpts, upkeepID, performData) if err != nil { failUnknown("failed to simulate perform upkeep: ", err) } + + // do tenderly simulation + rawCall, err := core.RegistryABI.Pack("simulatePerformUpkeep", upkeepID, performData) + if err != nil { + failUnknown("failed to pack raw simulatePerformUpkeep call", err) + } + addLink("simulatePerformUpkeep simulation", tenderlySimLink(k.cfg, chainID, blockNum, rawCall, registryAddress)) + if simulateResult.Success { resolveEligible() + } else { + resolveIneligible("simulate perform upkeep unsuccessful") } } @@ -466,11 +477,11 @@ func warning(msg string) { } func resolveIneligible(msg string) { - exit(fmt.Sprintf("✅ %s: this upkeep is not currently eligible", msg), nil, 0) + exit(fmt.Sprintf("❌ this upkeep is not eligible: %s", msg), nil, 0) } func resolveEligible() { - exit("❌ this upkeep is currently eligible", nil, 0) + exit("✅ this upkeep is eligible", nil, 0) } func rerun(msg string, err error) { diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go index 06a3e7b106b..ed3218ea405 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/interface.go @@ -1,13 +1,10 @@ package encoding import ( - "math/big" - ocr2keepers "github.com/smartcontractkit/chainlink-automation/pkg/v3/types" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" iregistry21 "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/i_keeper_registry_master_wrapper_2_1" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) const ( @@ -51,12 +48,8 @@ type UpkeepInfo = iregistry21.KeeperRegistryBase21UpkeepInfo type Packer interface { UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw string) (ocr2keepers.CheckResult, error) - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) UnpackPerformResult(raw string) (uint8, bool, error) UnpackLogTriggerConfig(raw []byte) (automation_utils_2_1.LogTriggerConfig, error) PackReport(report automation_utils_2_1.KeeperRegistryBase21Report) ([]byte, error) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistryBase21Report, error) - PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) - UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) - DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go index 57013a6277a..4a92b4a17ec 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer.go @@ -11,7 +11,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) // triggerWrapper is a wrapper for the different trigger types (log and condition triggers). @@ -67,35 +66,6 @@ func (p *abiPacker) UnpackCheckResult(payload ocr2keepers.UpkeepPayload, raw str return result, nil } -func (p *abiPacker) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { - return p.registryABI.Pack("getUpkeepPrivilegeConfig", upkeepId) -} - -func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) { - out, err := p.registryABI.Methods["getUpkeepPrivilegeConfig"].Outputs.UnpackValues(resp) - if err != nil { - return nil, fmt.Errorf("%w: unpack getUpkeepPrivilegeConfig return", err) - } - - bts := *abi.ConvertType(out[0], new([]byte)).(*[]byte) - - return bts, nil -} - -func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { - out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) - if err != nil { - return PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) - } - - upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) - rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) - failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) - gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) - - return NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil -} - func (p *abiPacker) UnpackPerformResult(raw string) (uint8, bool, error) { b, err := hexutil.Decode(raw) if err != nil { @@ -163,24 +133,6 @@ func (p *abiPacker) UnpackReport(raw []byte) (automation_utils_2_1.KeeperRegistr return report, nil } -// DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) -func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*mercury.StreamsLookupError, error) { - e := p.streamsABI.Errors["StreamsLookup"] - unpack, err := e.Unpack(data) - if err != nil { - return nil, fmt.Errorf("unpack error: %w", err) - } - errorParameters := unpack.([]interface{}) - - return &mercury.StreamsLookupError{ - FeedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), - Feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), - TimeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), - Time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), - ExtraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), - }, nil -} - // GetIneligibleCheckResultWithoutPerformData returns an ineligible check result with ineligibility reason and pipeline execution state but without perform data func GetIneligibleCheckResultWithoutPerformData(p ocr2keepers.UpkeepPayload, reason uint8, state uint8, retryable bool) ocr2keepers.CheckResult { return ocr2keepers.CheckResult{ diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go index 42fcd40d618..71c2755f150 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding/packer_test.go @@ -1,8 +1,6 @@ package encoding import ( - "encoding/json" - "errors" "fmt" "math/big" "testing" @@ -17,7 +15,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" automation21Utils "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/generated/automation_utils_2_1" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury" ) func TestPacker_PackReport(t *testing.T) { @@ -231,62 +228,6 @@ func TestPacker_UnpackPerformResult(t *testing.T) { } } -func TestPacker_UnpackCheckCallbackResult(t *testing.T) { - tests := []struct { - Name string - CallbackResp []byte - UpkeepNeeded bool - PerformData []byte - FailureReason uint8 - GasUsed *big.Int - ErrorString string - State uint8 - }{ - { - Name: "unpack upkeep needed", - CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: true, - PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(UpkeepFailureReasonNone), - GasUsed: big.NewInt(11796), - }, - { - Name: "unpack upkeep not needed", - CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: false, - PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - FailureReason: uint8(UpkeepFailureReasonUpkeepNotNeeded), - GasUsed: big.NewInt(13008), - }, - { - Name: "unpack malformed data", - CallbackResp: []byte{0, 0, 0, 23, 4, 163, 66, 91, 228, 102, 200, 84, 144, 233, 218, 44, 168, 192, 191, 253, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - UpkeepNeeded: false, - PerformData: nil, - ErrorString: "abi: improperly encoded boolean value: unpack checkUpkeep return: ", - State: PackUnpackDecodeFailed, - }, - } - for _, test := range tests { - t.Run(test.Name, func(t *testing.T) { - packer := NewAbiPacker() - - state, needed, pd, failureReason, gasUsed, err := packer.UnpackCheckCallbackResult(test.CallbackResp) - - if test.ErrorString != "" { - assert.EqualError(t, err, test.ErrorString+hexutil.Encode(test.CallbackResp)) - } else { - assert.Nil(t, err) - } - assert.Equal(t, test.UpkeepNeeded, needed) - assert.Equal(t, test.PerformData, pd) - assert.Equal(t, test.FailureReason, failureReason) - assert.Equal(t, test.GasUsed, gasUsed) - assert.Equal(t, test.State, state) - }) - } -} - func TestPacker_UnpackLogTriggerConfig(t *testing.T) { tests := []struct { name string @@ -349,130 +290,3 @@ func TestPacker_PackReport_UnpackReport(t *testing.T) { expected := "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000405060708000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000004050607080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000" assert.Equal(t, hexutil.Encode(res), expected) } - -func TestPacker_PackGetUpkeepPrivilegeConfig(t *testing.T) { - tests := []struct { - name string - upkeepId *big.Int - raw []byte - errored bool - }{ - { - name: "happy path", - upkeepId: func() *big.Int { - id, _ := new(big.Int).SetString("52236098515066839510538748191966098678939830769967377496848891145101407612976", 10) - - return id - }(), - raw: func() []byte { - b, _ := hexutil.Decode("0x19d97a94737c9583000000000000000000000001ea8ed6d0617dd5b3b87374020efaf030") - - return b - }(), - errored: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - packer := NewAbiPacker() - - b, err := packer.PackGetUpkeepPrivilegeConfig(test.upkeepId) - - if !test.errored { - require.NoError(t, err, "no error expected from packing") - - assert.Equal(t, test.raw, b, "raw bytes for output should match expected") - } else { - assert.NotNil(t, err, "error expected from packing function") - } - }) - } -} - -func TestPacker_UnpackGetUpkeepPrivilegeConfig(t *testing.T) { - tests := []struct { - name string - raw []byte - errored bool - }{ - { - name: "happy path", - raw: func() []byte { - b, _ := hexutil.Decode("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000177b226d657263757279456e61626c6564223a747275657d000000000000000000") - - return b - }(), - errored: false, - }, - { - name: "error empty config", - raw: func() []byte { - b, _ := hexutil.Decode("0x") - - return b - }(), - errored: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - packer := NewAbiPacker() - - b, err := packer.UnpackGetUpkeepPrivilegeConfig(test.raw) - - if !test.errored { - require.NoError(t, err, "should unpack bytes from abi encoded value") - - // the actual struct to unmarshal into is not available to this - // package so basic json encoding is the limit of the following test - var data map[string]interface{} - err = json.Unmarshal(b, &data) - - assert.NoError(t, err, "packed data should unmarshal using json encoding") - assert.Equal(t, []byte(`{"mercuryEnabled":true}`), b) - } else { - assert.NotNil(t, err, "error expected from unpack function") - } - }) - } -} - -func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { - tests := []struct { - name string - data []byte - expected *mercury.StreamsLookupError - state uint8 - err error - }{ - { - name: "success - decode to streams lookup", - data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), - expected: &mercury.StreamsLookupError{ - FeedParamKey: "feedIdHex", - Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, - TimeParamKey: "blockNumber", - Time: big.NewInt(37969589), - ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, - }, - }, - { - name: "failure - unpack error", - data: []byte{1, 2, 3, 4}, - err: errors.New("unpack error: invalid data for unpacking"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - packer := NewAbiPacker() - fl, err := packer.DecodeStreamsLookupRequest(tt.data) - assert.Equal(t, tt.expected, fl) - if tt.err != nil { - assert.Equal(t, tt.err.Error(), err.Error()) - } - }) - } -} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go index f9a3c001c66..10e77bf50ce 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury.go @@ -10,9 +10,13 @@ import ( "net/http" "time" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/patrickmn/go-cache" "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/models" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/core" + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) const ( @@ -68,13 +72,6 @@ type MercuryData struct { State MercuryUpkeepState } -type Packer interface { - UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) - PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) - UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) - DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) -} - type MercuryConfigProvider interface { Credentials() *models.MercuryCredentials IsUpkeepAllowed(string) (interface{}, bool) @@ -113,6 +110,70 @@ func (l *StreamsLookup) IsMercuryV03() bool { return l.FeedParamKey == FeedIDs } +// IsMercuryV03UsingBlockNumber is used to distinguish the batch path. It is used for Mercury V03 only func (l *StreamsLookup) IsMercuryV03UsingBlockNumber() bool { return l.TimeParamKey == BlockNumber } + +type Packer interface { + UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) + PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) + UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) + DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) +} + +type abiPacker struct { + registryABI abi.ABI + streamsABI abi.ABI +} + +func NewAbiPacker() *abiPacker { + return &abiPacker{registryABI: core.RegistryABI, streamsABI: core.StreamsCompatibleABI} +} + +// DecodeStreamsLookupRequest decodes the revert error StreamsLookup(string feedParamKey, string[] feeds, string feedParamKey, uint256 time, byte[] extraData) +func (p *abiPacker) DecodeStreamsLookupRequest(data []byte) (*StreamsLookupError, error) { + e := p.streamsABI.Errors["StreamsLookup"] + unpack, err := e.Unpack(data) + if err != nil { + return nil, fmt.Errorf("unpack error: %w", err) + } + errorParameters := unpack.([]interface{}) + + return &StreamsLookupError{ + FeedParamKey: *abi.ConvertType(errorParameters[0], new(string)).(*string), + Feeds: *abi.ConvertType(errorParameters[1], new([]string)).(*[]string), + TimeParamKey: *abi.ConvertType(errorParameters[2], new(string)).(*string), + Time: *abi.ConvertType(errorParameters[3], new(*big.Int)).(**big.Int), + ExtraData: *abi.ConvertType(errorParameters[4], new([]byte)).(*[]byte), + }, nil +} + +func (p *abiPacker) UnpackCheckCallbackResult(callbackResp []byte) (uint8, bool, []byte, uint8, *big.Int, error) { + out, err := p.registryABI.Methods["checkCallback"].Outputs.UnpackValues(callbackResp) + if err != nil { + return encoding.PackUnpackDecodeFailed, false, nil, 0, nil, fmt.Errorf("%w: unpack checkUpkeep return: %s", err, hexutil.Encode(callbackResp)) + } + + upkeepNeeded := *abi.ConvertType(out[0], new(bool)).(*bool) + rawPerformData := *abi.ConvertType(out[1], new([]byte)).(*[]byte) + failureReason := *abi.ConvertType(out[2], new(uint8)).(*uint8) + gasUsed := *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + + return encoding.NoPipelineError, upkeepNeeded, rawPerformData, failureReason, gasUsed, nil +} + +func (p *abiPacker) UnpackGetUpkeepPrivilegeConfig(resp []byte) ([]byte, error) { + out, err := p.registryABI.Methods["getUpkeepPrivilegeConfig"].Outputs.UnpackValues(resp) + if err != nil { + return nil, fmt.Errorf("%w: unpack getUpkeepPrivilegeConfig return", err) + } + + bts := *abi.ConvertType(out[0], new([]byte)).(*[]byte) + + return bts, nil +} + +func (p *abiPacker) PackGetUpkeepPrivilegeConfig(upkeepId *big.Int) ([]byte, error) { + return p.registryABI.Pack("getUpkeepPrivilegeConfig", upkeepId) +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go index baa939dbecc..6095e7b9466 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/mercury_test.go @@ -1,7 +1,17 @@ package mercury import ( + "encoding/json" + "errors" + "math/big" "testing" + + "github.com/stretchr/testify/require" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" + + "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/encoding" ) func TestGenerateHMACFn(t *testing.T) { @@ -47,3 +57,186 @@ func TestGenerateHMACFn(t *testing.T) { }) } } + +func TestPacker_DecodeStreamsLookupRequest(t *testing.T) { + tests := []struct { + name string + data []byte + expected *StreamsLookupError + state uint8 + err error + }{ + { + name: "success - decode to streams lookup", + data: hexutil.MustDecode("0xf055e4a200000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002400000000000000000000000000000000000000000000000000000000002435eb50000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000000966656564496448657800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000423078343535343438326435353533343432643431353234323439353435323535346432643534343535333534346534353534303030303030303030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000042307834323534343332643535353334343264343135323432343935343532353534643264353434353533353434653435353430303030303030303030303030303030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b626c6f636b4e756d62657200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000064000000000000000000000000"), + expected: &StreamsLookupError{ + FeedParamKey: "feedIdHex", + Feeds: []string{"0x4554482d5553442d415242495452554d2d544553544e45540000000000000000", "0x4254432d5553442d415242495452554d2d544553544e45540000000000000000"}, + TimeParamKey: "blockNumber", + Time: big.NewInt(37969589), + ExtraData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}, + }, + }, + { + name: "failure - unpack error", + data: []byte{1, 2, 3, 4}, + err: errors.New("unpack error: invalid data for unpacking"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + packer := NewAbiPacker() + fl, err := packer.DecodeStreamsLookupRequest(tt.data) + assert.Equal(t, tt.expected, fl) + if tt.err != nil { + assert.Equal(t, tt.err.Error(), err.Error()) + } + }) + } +} + +func TestPacker_UnpackGetUpkeepPrivilegeConfig(t *testing.T) { + tests := []struct { + name string + raw []byte + errored bool + }{ + { + name: "happy path", + raw: func() []byte { + b, _ := hexutil.Decode("0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000177b226d657263757279456e61626c6564223a747275657d000000000000000000") + + return b + }(), + errored: false, + }, + { + name: "error empty config", + raw: func() []byte { + b, _ := hexutil.Decode("0x") + + return b + }(), + errored: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + packer := NewAbiPacker() + + b, err := packer.UnpackGetUpkeepPrivilegeConfig(test.raw) + + if !test.errored { + require.NoError(t, err, "should unpack bytes from abi encoded value") + + // the actual struct to unmarshal into is not available to this + // package so basic json encoding is the limit of the following test + var data map[string]interface{} + err = json.Unmarshal(b, &data) + + assert.NoError(t, err, "packed data should unmarshal using json encoding") + assert.Equal(t, []byte(`{"mercuryEnabled":true}`), b) + } else { + assert.NotNil(t, err, "error expected from unpack function") + } + }) + } +} + +func TestPacker_PackGetUpkeepPrivilegeConfig(t *testing.T) { + tests := []struct { + name string + upkeepId *big.Int + raw []byte + errored bool + }{ + { + name: "happy path", + upkeepId: func() *big.Int { + id, _ := new(big.Int).SetString("52236098515066839510538748191966098678939830769967377496848891145101407612976", 10) + + return id + }(), + raw: func() []byte { + b, _ := hexutil.Decode("0x19d97a94737c9583000000000000000000000001ea8ed6d0617dd5b3b87374020efaf030") + + return b + }(), + errored: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + packer := NewAbiPacker() + + b, err := packer.PackGetUpkeepPrivilegeConfig(test.upkeepId) + + if !test.errored { + require.NoError(t, err, "no error expected from packing") + + assert.Equal(t, test.raw, b, "raw bytes for output should match expected") + } else { + assert.NotNil(t, err, "error expected from packing function") + } + }) + } +} + +func TestPacker_UnpackCheckCallbackResult(t *testing.T) { + tests := []struct { + Name string + CallbackResp []byte + UpkeepNeeded bool + PerformData []byte + FailureReason uint8 + GasUsed *big.Int + ErrorString string + State uint8 + }{ + { + Name: "unpack upkeep needed", + CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: true, + PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + FailureReason: uint8(encoding.UpkeepFailureReasonNone), + GasUsed: big.NewInt(11796), + }, + { + Name: "unpack upkeep not needed", + CallbackResp: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: false, + PerformData: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 10, 11, 21, 31, 41, 15, 16, 17, 18, 19, 13, 14, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120, 111, 101, 122, 90, 54, 44, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + FailureReason: uint8(encoding.UpkeepFailureReasonUpkeepNotNeeded), + GasUsed: big.NewInt(13008), + }, + { + Name: "unpack malformed data", + CallbackResp: []byte{0, 0, 0, 23, 4, 163, 66, 91, 228, 102, 200, 84, 144, 233, 218, 44, 168, 192, 191, 253, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + UpkeepNeeded: false, + PerformData: nil, + ErrorString: "abi: improperly encoded boolean value: unpack checkUpkeep return: ", + State: encoding.PackUnpackDecodeFailed, + }, + } + for _, test := range tests { + t.Run(test.Name, func(t *testing.T) { + packer := NewAbiPacker() + + state, needed, pd, failureReason, gasUsed, err := packer.UnpackCheckCallbackResult(test.CallbackResp) + + if test.ErrorString != "" { + assert.EqualError(t, err, test.ErrorString+hexutil.Encode(test.CallbackResp)) + } else { + assert.Nil(t, err) + } + assert.Equal(t, test.UpkeepNeeded, needed) + assert.Equal(t, test.PerformData, pd) + assert.Equal(t, test.FailureReason, failureReason) + assert.Equal(t, test.GasUsed, gasUsed) + assert.Equal(t, test.State, state) + }) + } +} diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go index d64a9e7ef83..e1bb69f33d8 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams.go @@ -66,7 +66,6 @@ type UpkeepPrivilegeConfig struct { } func NewStreamsLookup( - packer mercury.Packer, mercuryConfig mercury.MercuryConfigProvider, blockSubscriber latestBlockProvider, client contextCaller, @@ -74,6 +73,8 @@ func NewStreamsLookup( lggr logger.Logger) *streams { httpClient := http.DefaultClient threadCtrl := utils.NewThreadControl() + packer := mercury.NewAbiPacker() + return &streams{ packer: packer, mercuryConfig: mercuryConfig, @@ -231,7 +232,7 @@ func (s *streams) CheckCallback(ctx context.Context, values [][]byte, lookup *me checkResults[i].IneligibilityReason = uint8(mercury.MercuryUpkeepFailureReasonNone) checkResults[i].Eligible = true checkResults[i].PerformData = performData - s.lggr.Infof("at block %d upkeep %s requested time %s successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) + s.lggr.Infof("at block %d upkeep %s requested time %s CheckCallback successful with perform data: %s", lookup.Block, lookup.UpkeepId, lookup.Time, hexutil.Encode(performData)) return nil } @@ -288,6 +289,7 @@ func (s *streams) AllowedToUseMercury(opts *bind.CallOpts, upkeepId *big.Int) (s var upkeepPrivilegeConfigBytes []byte upkeepPrivilegeConfigBytes, err = s.packer.UnpackGetUpkeepPrivilegeConfig(resultBytes) + if err != nil { return mercury.PackUnpackDecodeFailed, mercury.MercuryUpkeepFailureReasonNone, false, false, fmt.Errorf("failed to get upkeep privilege config: %v", err) } diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go index caa3efb974b..54124b7c1a3 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/mercury/streams/streams_test.go @@ -102,14 +102,12 @@ func (r *mockRegistry) CheckCallback(opts *bind.CallOpts, id *big.Int, values [] // setups up a streams object for tests. func setupStreams(t *testing.T) *streams { lggr := logger.TestLogger(t) - packer := encoding.NewAbiPacker() mercuryConfig := new(MockMercuryConfigProvider) blockSubscriber := new(MockBlockSubscriber) registry := &mockRegistry{} client := evmClientMocks.NewClient(t) streams := NewStreamsLookup( - packer, mercuryConfig, blockSubscriber, client, diff --git a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go index bb05b8029fe..39862bb7539 100644 --- a/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go +++ b/core/services/ocr2/plugins/ocr2keeper/evmregistry/v21/registry.go @@ -94,7 +94,6 @@ func NewEvmRegistry( AllowListCache: cache.New(defaultAllowListExpiration, cleanupInterval), pluginRetryCache: cache.New(defaultPluginRetryExpiration, cleanupInterval), } - hc := http.DefaultClient return &EvmRegistry{ @@ -115,7 +114,7 @@ func NewEvmRegistry( logEventProvider: logEventProvider, bs: blockSub, finalityDepth: finalityDepth, - streams: streams.NewStreamsLookup(packer, mercuryConfig, blockSub, client.Client(), registry, lggr), + streams: streams.NewStreamsLookup(mercuryConfig, blockSub, client.Client(), registry, lggr), } }