Skip to content

Commit

Permalink
Fix GetLatestValueWithHeadData
Browse files Browse the repository at this point in the history
  • Loading branch information
ilija42 committed Nov 12, 2024
1 parent 6659c2e commit c8e4804
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 59 deletions.
17 changes: 17 additions & 0 deletions core/chains/evm/types/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import (
pkgerrors "github.com/pkg/errors"
"github.com/ugorji/go/codec"

chainagnostictypes "github.com/smartcontractkit/chainlink-common/pkg/types"
"github.com/smartcontractkit/chainlink-common/pkg/utils/hex"

htrktypes "github.com/smartcontractkit/chainlink/v2/common/headtracker/types"
commontypes "github.com/smartcontractkit/chainlink/v2/common/types"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/types/internal/blocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
Expand Down Expand Up @@ -189,6 +191,9 @@ func (h *Head) ChainString() string {

// String returns a string representation of this head
func (h *Head) String() string {
if h == nil {
return "<nil>"
}
return fmt.Sprintf("Head{Number: %d, Hash: %s, ParentHash: %s}", h.ToInt(), h.Hash.Hex(), h.ParentHash.Hex())
}

Expand Down Expand Up @@ -316,6 +321,18 @@ func (h *Head) MarshalJSON() ([]byte, error) {
return json.Marshal(jsonHead)
}

func (h *Head) ToChainAgnosticHead() *chainagnostictypes.Head {
if h == nil {
return nil
}

return &chainagnostictypes.Head{
Height: fmt.Sprint(10),
Hash: h.Hash.Bytes(),
Timestamp: uint64(h.Timestamp.Unix()),
}
}

// Block represents an ethereum block
// This type is only used for the block history estimator, and can be expensive to unmarshal. Don't add unnecessary fields here.
type Block struct {
Expand Down
46 changes: 21 additions & 25 deletions core/services/relay/evm/chain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"maps"
"slices"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -198,7 +197,8 @@ func (cr *chainReader) GetLatestValue(ctx context.Context, readName string, conf

ptrToValue, isValue := returnVal.(*values.Value)
if !isValue {
return binding.GetLatestValue(ctx, common.HexToAddress(address), confidenceLevel, params, returnVal)
_, err = binding.GetLatestValueWithHeadData(ctx, common.HexToAddress(address), confidenceLevel, params, returnVal)
return err
}

contractType, err := cr.CreateContractType(readName, false)
Expand All @@ -220,39 +220,35 @@ func (cr *chainReader) GetLatestValue(ctx context.Context, readName string, conf
return nil
}

func (cr *chainReader) GetLatestValueWithHeadData(ctx context.Context, readName string, confidenceLevel primitives.ConfidenceLevel, params any, returnVal any) (*commontypes.Head, error) {
if err := cr.GetLatestValue(ctx, readName, confidenceLevel, params, returnVal); err != nil {
func (cr *chainReader) GetLatestValueWithHeadData(ctx context.Context, readName string, confidenceLevel primitives.ConfidenceLevel, params any, returnVal any) (head *commontypes.Head, err error) {
binding, address, err := cr.bindings.GetReader(readName)
if err != nil {
return nil, err
}

latestHead, finalizedHead, err := cr.ht.LatestAndFinalizedBlock(ctx)
ptrToValue, isValue := returnVal.(*values.Value)
if !isValue {
return binding.GetLatestValueWithHeadData(ctx, common.HexToAddress(address), confidenceLevel, params, returnVal)
}

contractType, err := cr.CreateContractType(readName, false)
if err != nil {
return nil, err
}

switch confidenceLevel {
case primitives.Finalized:
if finalizedHead != nil {
return ethHeadToCommonHead(finalizedHead), nil
}
return nil, nil
case primitives.Unconfirmed:
if latestHead != nil {
return ethHeadToCommonHead(latestHead), nil
}
return nil, nil
default:
return nil, errors.New("unsupported confidence level")
head, err = cr.GetLatestValueWithHeadData(ctx, readName, confidenceLevel, params, contractType)
if err != nil {
return nil, err
}
}

func ethHeadToCommonHead(finalizedHead *evmtypes.Head) *commontypes.Head {
return &commontypes.Head{
Height: strconv.FormatInt(finalizedHead.Number, 10),
Hash: finalizedHead.Hash.Bytes(),
//nolint:gosec // disable G115
Timestamp: uint64(finalizedHead.Timestamp.Unix()),
value, err := values.Wrap(contractType)
if err != nil {
return nil, err
}

*ptrToValue = value

return head, nil
}

func (cr *chainReader) BatchGetLatestValues(ctx context.Context, request commontypes.BatchGetLatestValuesRequest) (commontypes.BatchGetLatestValuesResult, error) {
Expand Down
2 changes: 1 addition & 1 deletion core/services/relay/evm/read/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (

type Reader interface {
BatchCall(address common.Address, params, retVal any) (Call, error)
GetLatestValue(ctx context.Context, addr common.Address, confidence primitives.ConfidenceLevel, params, returnVal any) error
GetLatestValueWithHeadData(ctx context.Context, addr common.Address, confidence primitives.ConfidenceLevel, params, returnVal any) (*commontypes.Head, error)
QueryKey(context.Context, common.Address, query.KeyFilter, query.LimitAndSort, any) ([]commontypes.Sequence, error)

Bind(context.Context, ...common.Address) error
Expand Down
27 changes: 15 additions & 12 deletions core/services/relay/evm/read/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (b *EventBinding) BatchCall(_ common.Address, _, _ any) (Call, error) {
return Call{}, fmt.Errorf("%w: events are not yet supported in batch get latest values", commontypes.ErrInvalidType)
}

func (b *EventBinding) GetLatestValue(ctx context.Context, address common.Address, confidenceLevel primitives.ConfidenceLevel, params, into any) (err error) {
func (b *EventBinding) GetLatestValueWithHeadData(ctx context.Context, address common.Address, confidenceLevel primitives.ConfidenceLevel, params, into any) (head *commontypes.Head, err error) {
var (
confs evmtypes.Confirmations
result *string
Expand All @@ -256,51 +256,54 @@ func (b *EventBinding) GetLatestValue(ctx context.Context, address common.Addres
}()

if err = b.validateBound(address); err != nil {
return err
return nil, err
}

confs, err = confidenceToConfirmations(b.confirmationsMapping, confidenceLevel)
if err != nil {
return err
return nil, err
}

topicTypeID := codec.WrapItemType(b.contractName, b.eventName, true)

onChainTypedVal, err := b.toNativeOnChainType(topicTypeID, params)
if err != nil {
return err
return nil, err
}

filterTopics, err := b.extractFilterTopics(topicTypeID, onChainTypedVal)
if err != nil {
return err
return nil, err
}

var log *logpoller.Log
if len(filterTopics) != 0 {
var hashedTopics []common.Hash
hashedTopics, err = b.hashTopics(topicTypeID, filterTopics)
if err != nil {
return err
return nil, err
}

if log, err = b.getLatestLog(ctx, address, confs, hashedTopics); err != nil {
return err
return nil, err
}
} else {
if log, err = b.lp.LatestLogByEventSigWithConfs(ctx, b.hash, address, confs); err != nil {
return wrapInternalErr(err)
return nil, wrapInternalErr(err)
}
}

if err := b.decodeLog(ctx, log, into); err != nil {
if err = b.decodeLog(ctx, log, into); err != nil {
encoded := hex.EncodeToString(log.Data)
result = &encoded

return err
return nil, err
}

return nil
return &commontypes.Head{
Height: fmt.Sprint(log.BlockNumber),
Hash: log.BlockHash.Bytes(),
Timestamp: uint64(log.BlockTimestamp.Unix()),
}, nil
}

func (b *EventBinding) QueryKey(ctx context.Context, address common.Address, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) (sequences []commontypes.Sequence, err error) {
Expand Down
47 changes: 26 additions & 21 deletions core/services/relay/evm/read/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,19 @@ func (b *MethodBinding) BatchCall(address common.Address, params, retVal any) (C
}, nil
}

func (b *MethodBinding) GetLatestValue(ctx context.Context, addr common.Address, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) error {
func (b *MethodBinding) GetLatestValueWithHeadData(ctx context.Context, addr common.Address, confidenceLevel primitives.ConfidenceLevel, params, returnVal any) (*commontypes.Head, error) {
if !b.isBound(addr) {
return fmt.Errorf("%w: %w", commontypes.ErrInvalidConfig, newUnboundAddressErr(addr.Hex(), b.contractName, b.method))
return nil, fmt.Errorf("%w: %w", commontypes.ErrInvalidConfig, newUnboundAddressErr(addr.Hex(), b.contractName, b.method))
}

block, err := b.blockNumberFromConfidence(ctx, confidenceLevel)
block, confirmations, err := b.blockAndConfirmationsFromConfidence(ctx, confidenceLevel)
if err != nil {
return err
return nil, err
}

var blockNum *big.Int
if block != nil && confirmations != evmtypes.Unconfirmed {
blockNum = big.NewInt(block.Number)
}

data, err := b.codec.Encode(ctx, params, codec.WrapItemType(b.contractName, b.method, true))
Expand All @@ -141,9 +146,9 @@ func (b *MethodBinding) GetLatestValue(ctx context.Context, addr common.Address,
ReadName: b.method,
Params: params,
ReturnVal: returnVal,
}, block.String(), false)
}, blockNum.String(), false)

return callErr
return nil, callErr
}

callMsg := ethereum.CallMsg{
Expand All @@ -152,7 +157,7 @@ func (b *MethodBinding) GetLatestValue(ctx context.Context, addr common.Address,
Data: data,
}

bytes, err := b.client.CallContract(ctx, callMsg, block)
bytes, err := b.client.CallContract(ctx, callMsg, blockNum)
if err != nil {
callErr := newErrorFromCall(
fmt.Errorf("%w: contract call: %s", commontypes.ErrInvalidType, err.Error()),
Expand All @@ -162,9 +167,9 @@ func (b *MethodBinding) GetLatestValue(ctx context.Context, addr common.Address,
ReadName: b.method,
Params: params,
ReturnVal: returnVal,
}, block.String(), false)
}, blockNum.String(), false)

return callErr
return nil, callErr
}

if err = b.codec.Decode(ctx, bytes, returnVal, codec.WrapItemType(b.contractName, b.method, false)); err != nil {
Expand All @@ -176,15 +181,15 @@ func (b *MethodBinding) GetLatestValue(ctx context.Context, addr common.Address,
ReadName: b.method,
Params: params,
ReturnVal: returnVal,
}, block.String(), false)
}, blockNum.String(), false)

strResult := hexutil.Encode(bytes)
callErr.Result = &strResult

return callErr
return nil, callErr
}

return nil
return block.ToChainAgnosticHead(), nil
}

func (b *MethodBinding) QueryKey(
Expand All @@ -200,31 +205,31 @@ func (b *MethodBinding) QueryKey(
func (b *MethodBinding) Register(_ context.Context) error { return nil }
func (b *MethodBinding) Unregister(_ context.Context) error { return nil }

func (b *MethodBinding) blockNumberFromConfidence(ctx context.Context, confidenceLevel primitives.ConfidenceLevel) (*big.Int, error) {
func (b *MethodBinding) blockAndConfirmationsFromConfidence(ctx context.Context, confidenceLevel primitives.ConfidenceLevel) (*evmtypes.Head, evmtypes.Confirmations, error) {
confirmations, err := confidenceToConfirmations(b.confirmationsMapping, confidenceLevel)
if err != nil {
err = fmt.Errorf("%w: contract: %s; method: %s;", err, b.contractName, b.method)
err = fmt.Errorf("%w: contract: %s; method: %s", err, b.contractName, b.method)
if confidenceLevel == primitives.Unconfirmed {
b.lggr.Debugw("Falling back to default contract call behaviour that calls latest state", "contract", b.contractName, "method", b.method, "err", err)

return nil, nil
return nil, 0, err
}

return nil, err
return nil, 0, err
}

_, finalized, err := b.ht.LatestAndFinalizedBlock(ctx)
latest, finalized, err := b.ht.LatestAndFinalizedBlock(ctx)
if err != nil {
return nil, fmt.Errorf("%w: head tracker: %w", commontypes.ErrInternal, err)
return nil, 0, fmt.Errorf("%w: head tracker: %w", commontypes.ErrInternal, err)
}

if confirmations == evmtypes.Finalized {
return big.NewInt(finalized.Number), nil
return finalized, confirmations, nil
} else if confirmations == evmtypes.Unconfirmed {
return nil, nil
return latest, confirmations, nil
}

return nil, fmt.Errorf("%w: [unknown evm confirmations]: %v; contract: %s; method: %s;", commontypes.ErrInvalidConfig, confirmations, b.contractName, b.method)
return nil, 0, fmt.Errorf("%w: [unknown evm confirmations]: %v; contract: %s; method: %s", commontypes.ErrInvalidConfig, confirmations, b.contractName, b.method)
}

func (b *MethodBinding) isBound(binding common.Address) bool {
Expand Down

0 comments on commit c8e4804

Please sign in to comment.