Skip to content

Commit

Permalink
Version abstraction part 2 (#180)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Yang <[email protected]>
Co-authored-by: dimkouv <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2023
1 parent cc2810f commit 8e9719d
Show file tree
Hide file tree
Showing 78 changed files with 4,214 additions and 2,656 deletions.
3 changes: 1 addition & 2 deletions core/services/metatx/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/ethkey"
"github.com/smartcontractkit/chainlink/v2/core/services/metatx"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers"
integrationtesthelpers "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/testhelpers/integration"
)
Expand Down Expand Up @@ -235,7 +234,7 @@ merge [type=merge left="{}" right="{\\\"%s\\\":$(link_parse), \\\"%s\\\":$(eth_p

executionLogs := ccipContracts.AllNodesHaveExecutedSeqNums(t, geCurrentSeqNum, geCurrentSeqNum)
assert.Len(t, executionLogs, 1)
ccipContracts.AssertExecState(t, executionLogs[0], abihelpers.ExecutionStateSuccess)
ccipContracts.AssertExecState(t, executionLogs[0], testhelpers.ExecutionStateSuccess)

// source token is locked in the token pool
lockedTokenBal, err := sourceToken.BalanceOf(nil, sourcePoolAddress)
Expand Down
223 changes: 15 additions & 208 deletions core/services/ocr2/plugins/ccip/abihelpers/abi_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,101 +10,40 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/pkg/errors"
"github.com/smartcontractkit/libocr/gethwrappers2/ocr2aggregator"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"

"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/price_registry"
"github.com/smartcontractkit/chainlink/v2/core/utils"
)

// MessageExecutionState defines the execution states of CCIP messages.
type MessageExecutionState uint8

const (
ExecutionStateUntouched MessageExecutionState = iota
ExecutionStateInProgress
ExecutionStateSuccess
ExecutionStateFailure
)

// TODO: Deprecate in favour of version specific types
var EventSignatures struct {
// CommitStore
ReportAccepted common.Hash
// OffRamp
ExecutionStateChanged common.Hash
PoolAdded common.Hash
PoolRemoved common.Hash

// PriceRegistry
UsdPerUnitGasUpdated common.Hash
UsdPerTokenUpdated common.Hash
FeeTokenAdded common.Hash
FeeTokenRemoved common.Hash

// offset || priceUpdatesOffset || minSeqNum || maxSeqNum || merkleRoot
ReportAcceptedMaxSequenceNumberWord int
// sig || seqNum || messageId || ...
ExecutionStateChangedSequenceNumberIndex int
}

var (
MessageArgs abi.Arguments
TokenAmountsArgs abi.Arguments
CommitReportArgs abi.Arguments
ExecutionReportArgs abi.Arguments
)

func GetIDOrPanic(name string, abi2 abi.ABI) common.Hash {
func MustGetEventID(name string, abi2 abi.ABI) common.Hash {
event, ok := abi2.Events[name]
if !ok {
panic(fmt.Sprintf("missing event %s", name))
}
return event.ID
}

func init() {
commitStoreABI, err := abi.JSON(strings.NewReader(commit_store.CommitStoreABI))
if err != nil {
panic(err)
}
EventSignatures.ReportAccepted = GetIDOrPanic("ReportAccepted", commitStoreABI)
EventSignatures.ReportAcceptedMaxSequenceNumberWord = 3

offRampABI, err := abi.JSON(strings.NewReader(evm_2_evm_offramp.EVM2EVMOffRampABI))
if err != nil {
panic(err)
}
EventSignatures.ExecutionStateChanged = GetIDOrPanic("ExecutionStateChanged", offRampABI)
EventSignatures.ExecutionStateChangedSequenceNumberIndex = 1
EventSignatures.PoolAdded = GetIDOrPanic("PoolAdded", offRampABI)
EventSignatures.PoolRemoved = GetIDOrPanic("PoolRemoved", offRampABI)

priceRegistryABI, err := abi.JSON(strings.NewReader(price_registry.PriceRegistryABI))
if err != nil {
panic(err)
func MustGetEventInputs(name string, abi2 abi.ABI) abi.Arguments {
m, ok := abi2.Events[name]
if !ok {
panic(fmt.Sprintf("missing event %s", name))
}
EventSignatures.UsdPerUnitGasUpdated = GetIDOrPanic("UsdPerUnitGasUpdated", priceRegistryABI)
EventSignatures.UsdPerTokenUpdated = GetIDOrPanic("UsdPerTokenUpdated", priceRegistryABI)
EventSignatures.FeeTokenAdded = GetIDOrPanic("FeeTokenAdded", priceRegistryABI)
EventSignatures.FeeTokenRemoved = GetIDOrPanic("FeeTokenRemoved", priceRegistryABI)

CommitReportArgs = commitStoreABI.Events["ReportAccepted"].Inputs
return m.Inputs
}

manuallyExecuteMethod, ok := offRampABI.Methods["manuallyExecute"]
func MustGetMethodInputs(name string, abi2 abi.ABI) abi.Arguments {
m, ok := abi2.Methods[name]
if !ok {
panic("missing event 'manuallyExecute'")
panic(fmt.Sprintf("missing method %s", name))
}
ExecutionReportArgs = manuallyExecuteMethod.Inputs[:1]
return m.Inputs
}

func MessagesFromExecutionReport(report types.Report) ([]evm_2_evm_offramp.InternalEVM2EVMMessage, error) {
decodedExecutionReport, err := DecodeExecutionReport(report)
func MustParseABI(abiStr string) abi.ABI {
abiParsed, err := abi.JSON(strings.NewReader(abiStr))
if err != nil {
return nil, err
panic(err)
}
return decodedExecutionReport.Messages, nil
return abiParsed
}

// ProofFlagsToBits transforms a list of boolean proof flags to a *big.Int
Expand All @@ -119,138 +58,6 @@ func ProofFlagsToBits(proofFlags []bool) *big.Int {
return encodedFlags
}

func EncodeExecutionReport(execReport evm_2_evm_offramp.InternalExecutionReport) ([]byte, error) {
return ExecutionReportArgs.PackValues([]interface{}{&execReport})
}

func DecodeExecutionReport(report []byte) (evm_2_evm_offramp.InternalExecutionReport, error) {
unpacked, err := ExecutionReportArgs.Unpack(report)
if err != nil {
return evm_2_evm_offramp.InternalExecutionReport{}, err
}
if len(unpacked) == 0 {
return evm_2_evm_offramp.InternalExecutionReport{}, errors.New("assumptionViolation: expected at least one element")
}

// Must be anonymous struct here
erStruct, ok := unpacked[0].(struct {
Messages []struct {
SourceChainSelector uint64 `json:"sourceChainSelector"`
Sender common.Address `json:"sender"`
Receiver common.Address `json:"receiver"`
SequenceNumber uint64 `json:"sequenceNumber"`
GasLimit *big.Int `json:"gasLimit"`
Strict bool `json:"strict"`
Nonce uint64 `json:"nonce"`
FeeToken common.Address `json:"feeToken"`
FeeTokenAmount *big.Int `json:"feeTokenAmount"`
Data []uint8 `json:"data"`
TokenAmounts []struct {
Token common.Address `json:"token"`
Amount *big.Int `json:"amount"`
} `json:"tokenAmounts"`
SourceTokenData [][]byte `json:"sourceTokenData"`
MessageId [32]byte `json:"messageId"`
} `json:"messages"`
OffchainTokenData [][][]byte `json:"offchainTokenData"`
Proofs [][32]uint8 `json:"proofs"`
ProofFlagBits *big.Int `json:"proofFlagBits"`
})
if !ok {
return evm_2_evm_offramp.InternalExecutionReport{}, fmt.Errorf("got %T", unpacked[0])
}
var er evm_2_evm_offramp.InternalExecutionReport
er.Messages = []evm_2_evm_offramp.InternalEVM2EVMMessage{}

for _, msg := range erStruct.Messages {
var tokensAndAmounts []evm_2_evm_offramp.ClientEVMTokenAmount
for _, tokenAndAmount := range msg.TokenAmounts {
tokensAndAmounts = append(tokensAndAmounts, evm_2_evm_offramp.ClientEVMTokenAmount{
Token: tokenAndAmount.Token,
Amount: tokenAndAmount.Amount,
})
}
er.Messages = append(er.Messages, evm_2_evm_offramp.InternalEVM2EVMMessage{
SourceChainSelector: msg.SourceChainSelector,
SequenceNumber: msg.SequenceNumber,
FeeTokenAmount: msg.FeeTokenAmount,
Sender: msg.Sender,
Nonce: msg.Nonce,
GasLimit: msg.GasLimit,
Strict: msg.Strict,
Receiver: msg.Receiver,
Data: msg.Data,
TokenAmounts: tokensAndAmounts,
SourceTokenData: msg.SourceTokenData,
FeeToken: msg.FeeToken,
MessageId: msg.MessageId,
})
}

er.OffchainTokenData = erStruct.OffchainTokenData
er.Proofs = append(er.Proofs, erStruct.Proofs...)
// Unpack will populate with big.Int{false, <allocated empty nat>} for 0 values,
// which is different from the expected big.NewInt(0). Rebuild to the expected value for this case.
er.ProofFlagBits = new(big.Int).SetBytes(erStruct.ProofFlagBits.Bytes())
return er, nil
}

// EncodeCommitReport abi encodes an offramp.InternalCommitReport.
func EncodeCommitReport(commitReport commit_store.CommitStoreCommitReport) ([]byte, error) {
return CommitReportArgs.PackValues([]interface{}{commitReport})
}

// DecodeCommitReport abi decodes a types.Report to an CommitStoreCommitReport
func DecodeCommitReport(report []byte) (commit_store.CommitStoreCommitReport, error) {
unpacked, err := CommitReportArgs.Unpack(report)
if err != nil {
return commit_store.CommitStoreCommitReport{}, err
}
if len(unpacked) != 1 {
return commit_store.CommitStoreCommitReport{}, errors.New("expected single struct value")
}

commitReport, ok := unpacked[0].(struct {
PriceUpdates struct {
TokenPriceUpdates []struct {
SourceToken common.Address `json:"sourceToken"`
UsdPerToken *big.Int `json:"usdPerToken"`
} `json:"tokenPriceUpdates"`
DestChainSelector uint64 `json:"destChainSelector"`
UsdPerUnitGas *big.Int `json:"usdPerUnitGas"`
} `json:"priceUpdates"`
Interval struct {
Min uint64 `json:"min"`
Max uint64 `json:"max"`
} `json:"interval"`
MerkleRoot [32]byte `json:"merkleRoot"`
})
if !ok {
return commit_store.CommitStoreCommitReport{}, errors.Errorf("invalid commit report got %T", unpacked[0])
}

var tokenPriceUpdates []commit_store.InternalTokenPriceUpdate
for _, u := range commitReport.PriceUpdates.TokenPriceUpdates {
tokenPriceUpdates = append(tokenPriceUpdates, commit_store.InternalTokenPriceUpdate{
SourceToken: u.SourceToken,
UsdPerToken: u.UsdPerToken,
})
}

return commit_store.CommitStoreCommitReport{
PriceUpdates: commit_store.InternalPriceUpdates{
DestChainSelector: commitReport.PriceUpdates.DestChainSelector,
UsdPerUnitGas: commitReport.PriceUpdates.UsdPerUnitGas,
TokenPriceUpdates: tokenPriceUpdates,
},
Interval: commit_store.CommitStoreInterval{
Min: commitReport.Interval.Min,
Max: commitReport.Interval.Max,
},
MerkleRoot: commitReport.MerkleRoot,
}, nil
}

type AbiDefined interface {
AbiString() string
}
Expand Down
54 changes: 0 additions & 54 deletions core/services/ocr2/plugins/ccip/abihelpers/abi_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@ import (
"fmt"
"math"
"math/big"
"math/rand"
"testing"

"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/commit_store"
"github.com/smartcontractkit/chainlink/v2/core/utils"

"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/assert"
"github.com/test-go/testify/require"

"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_offramp"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
)

func TestProofFlagToBits(t *testing.T) {
Expand Down Expand Up @@ -62,52 +54,6 @@ func TestProofFlagToBits(t *testing.T) {
}
}

func TestCommitReportEncoding(t *testing.T) {
report := commit_store.CommitStoreCommitReport{
PriceUpdates: commit_store.InternalPriceUpdates{
TokenPriceUpdates: []commit_store.InternalTokenPriceUpdate{
{
SourceToken: utils.RandomAddress(),
UsdPerToken: big.NewInt(9e18),
},
},
DestChainSelector: rand.Uint64(),
UsdPerUnitGas: big.NewInt(2000e9),
},
MerkleRoot: [32]byte{123},
Interval: commit_store.CommitStoreInterval{Min: 1, Max: 10},
}

encodedReport, err := EncodeCommitReport(report)
require.NoError(t, err)

decodedReport, err := DecodeCommitReport(encodedReport)
require.NoError(t, err)
require.Equal(t, report, decodedReport)
}

func TestExecutionReportEncoding(t *testing.T) {
// Note could consider some fancier testing here (fuzz/property)
// but I think that would essentially be testing geth's abi library
// as our encode/decode is a thin wrapper around that.
report := evm_2_evm_offramp.InternalExecutionReport{
Messages: []evm_2_evm_offramp.InternalEVM2EVMMessage{},
OffchainTokenData: [][][]byte{{}},
Proofs: [][32]byte{testutils.Random32Byte()},
ProofFlagBits: big.NewInt(133),
}
encodeExecutionReport, err := EncodeExecutionReport(evm_2_evm_offramp.InternalExecutionReport{
Messages: report.Messages,
OffchainTokenData: report.OffchainTokenData,
Proofs: report.Proofs,
ProofFlagBits: report.ProofFlagBits,
})
require.NoError(t, err)
decodeCommitReport, err := DecodeExecutionReport(encodeExecutionReport)
require.NoError(t, err)
require.Equal(t, report, decodeCommitReport)
}

func TestEvmWord(t *testing.T) {
testCases := []struct {
inp uint64
Expand Down
Loading

0 comments on commit 8e9719d

Please sign in to comment.