Skip to content

Commit

Permalink
Merge pull request #11641 from smartcontractkit/rtinianov_cleaner_reader
Browse files Browse the repository at this point in the history
Simplify chain reader's bindings.
  • Loading branch information
nolag authored Dec 21, 2023
2 parents 22e6652 + a2e92cd commit 836ea49
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 203 deletions.
2 changes: 1 addition & 1 deletion core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ require (
github.com/shirou/gopsutil/v3 v3.23.11 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect
github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214180711-f4668ff89fe9 // indirect
github.com/smartcontractkit/chainlink-common v0.1.7-0.20231220205429-813aebce838e // indirect
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 // indirect
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 // indirect
github.com/smartcontractkit/chainlink-feeds v0.0.0-20231213175155-181d4a261f5d // indirect
Expand Down
4 changes: 2 additions & 2 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1148,8 +1148,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-automation v1.0.1 h1:vVjBFq2Zsz21kPy1Pb0wpjF9zrbJX+zjXphDeeR4XZk=
github.com/smartcontractkit/chainlink-automation v1.0.1/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214180711-f4668ff89fe9 h1:dca0f2dpHOPPu6raYK5nJU0A5P7JkXM5PbMGNezg2NI=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20231214180711-f4668ff89fe9/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20231220205429-813aebce838e h1:t9yp8N8WgbCspo0JOIJaxyYNeMEzpNhQXEyr708bFT4=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20231220205429-813aebce838e/go.mod h1:IdlfCN9rUs8Q/hrOYe8McNBIwEOHEsi0jilb3Cw77xs=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 h1:iu1pNbUoSDTrp+7BUtfTygZ2C0f5C2ZOBQhIoJjp+S0=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679/go.mod h1:2Jx7bTEk4ujFQdsZpZq3A0BydvaVPs6mX8clUfxHOEM=
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20231204152908-a6e3fe8ff2a1 h1:xYqRgZO0nMSO8CBCMR0r3WA+LZ4kNL8a6bnbyk/oBtQ=
Expand Down
156 changes: 156 additions & 0 deletions core/services/relay/evm/binding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package evm

import (
"context"
"fmt"
"strings"

"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"

commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"

evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
)

type readBinding interface {
GetLatestValue(ctx context.Context, params any) ([]byte, error)
Bind(binding commontypes.BoundContract) error
SetCodec(codec commontypes.Codec)
Register() error
Unregister() error
}

type methodBinding struct {
address common.Address
contractName string
method string
client evmclient.Client
codec commontypes.Codec
bound bool
}

var _ readBinding = &methodBinding{}

func (m *methodBinding) SetCodec(codec commontypes.Codec) {
m.codec = codec
}

func (m *methodBinding) Register() error {
return nil
}

func (m *methodBinding) Unregister() error {
return nil
}

func (m *methodBinding) GetLatestValue(ctx context.Context, params any) ([]byte, error) {
if !m.bound {
return nil, fmt.Errorf("%w: method not bound", commontypes.ErrInvalidType)
}

data, err := m.codec.Encode(ctx, params, wrapItemType(m.contractName, m.method, true))
if err != nil {
return nil, err
}

callMsg := ethereum.CallMsg{
To: &m.address,
From: m.address,
Data: data,
}

return m.client.CallContract(ctx, callMsg, nil)
}

func (m *methodBinding) Bind(binding commontypes.BoundContract) error {
m.address = common.HexToAddress(binding.Address)
m.bound = true
return nil
}

type eventBinding struct {
address common.Address
contractName string
eventName string
lp logpoller.LogPoller
hash common.Hash
codec commontypes.Codec
pending bool
subscribed bool
bound bool
}

func (e *eventBinding) SetCodec(codec commontypes.Codec) {
e.codec = codec
}

func (e *eventBinding) Register() error {
e.subscribed = true
if !e.bound {
return nil
}

if err := e.lp.RegisterFilter(logpoller.Filter{
Name: wrapItemType(e.contractName, e.eventName, false),
EventSigs: evmtypes.HashArray{e.hash},
Addresses: evmtypes.AddressArray{e.address},
}); err != nil {
return fmt.Errorf("%w: %w", commontypes.ErrInternal, err)
}
return nil
}

func (e *eventBinding) Unregister() error {
e.subscribed = false
if !e.bound {
return nil
}

if err := e.lp.UnregisterFilter(wrapItemType(e.contractName, e.eventName, false)); err != nil {
return fmt.Errorf("%w: %w", commontypes.ErrInternal, err)
}
return nil
}

var _ readBinding = &eventBinding{}

func (e *eventBinding) GetLatestValue(_ context.Context, _ any) ([]byte, error) {
if !e.bound {
return nil, fmt.Errorf("%w: event not bound", commontypes.ErrInvalidType)
}

confs := logpoller.Finalized
if e.pending {
confs = logpoller.Unconfirmed
}
log, err := e.lp.LatestLogByEventSigWithConfs(e.hash, e.address, confs)
if err != nil {
errStr := err.Error()
if strings.Contains(errStr, "not found") || strings.Contains(errStr, "no rows") {
return nil, nil
}
return nil, err
}

return log.Data, nil
}

func (e *eventBinding) Bind(binding commontypes.BoundContract) error {
wasSubscribed := e.subscribed
if wasSubscribed {
if err := e.Unregister(); err != nil {
return err
}
}
e.address = common.HexToAddress(binding.Address)
e.pending = binding.Pending
e.bound = true

if wasSubscribed {
return e.Register()
}
return nil
}
75 changes: 43 additions & 32 deletions core/services/relay/evm/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,59 @@ package evm
import (
"fmt"

"github.com/ethereum/go-ethereum/common"

"github.com/smartcontractkit/chainlink-common/pkg/types"
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
)

type Bindings map[string]methodBindings
// key is contract name
type contractBindings map[string]readBindings

func (b Bindings) addEvent(contractName, typeName string, evt common.Hash) error {
ae, err := b.getBinding(contractName, typeName, true)
if err != nil {
return err
}
// key is read name
type readBindings map[string]readBinding

ae.evt = &evt
return nil
}

func (b Bindings) getBinding(contractName, methodName string, isConfig bool) (*addrEvtBinding, error) {
errType := types.ErrInvalidType
if isConfig {
errType = types.ErrInvalidConfig
}
methodNames, ok := b[contractName]
if !ok {
return nil, fmt.Errorf("%w: contract %s not found", errType, contractName)
func (b contractBindings) GetReadBinding(contractName, readName string) (readBinding, error) {
rb, rbExists := b[contractName]
if !rbExists {
return nil, fmt.Errorf("%w: no contract named %s", commontypes.ErrInvalidType, contractName)
}

ae, ok := methodNames[methodName]
if !ok {
return nil, fmt.Errorf("%w: method %s not found in contract %s", errType, methodName, contractName)
reader, readerExists := rb[readName]
if !readerExists {
return nil, fmt.Errorf("%w: no readName named %s in contract %s", commontypes.ErrInvalidType, readName, contractName)
}

return ae, nil
return reader, nil
}

type methodBindings map[string]*addrEvtBinding
func (b contractBindings) AddReadBinding(contractName, readName string, reader readBinding) {
rbs, rbsExists := b[contractName]
if !rbsExists {
rbs = readBindings{}
b[contractName] = rbs
}
rbs[readName] = reader
}

func NewAddrEvtFromAddress(address common.Address) *addrEvtBinding {
return &addrEvtBinding{addr: address}
func (b contractBindings) Bind(boundContracts []commontypes.BoundContract) error {
for _, bc := range boundContracts {
rbs, rbsExist := b[bc.Name]
if !rbsExist {
return fmt.Errorf("%w: no contract named %s", commontypes.ErrInvalidConfig, bc.Name)
}
for _, r := range rbs {
if err := r.Bind(bc); err != nil {
return err
}
}
}
return nil
}

type addrEvtBinding struct {
addr common.Address
evt *common.Hash
func (b contractBindings) ForEach(fn func(readBinding) error) error {
for _, rbs := range b {
for _, rb := range rbs {
if err := fn(rb); err != nil {
return err
}
}
}
return nil
}
Loading

0 comments on commit 836ea49

Please sign in to comment.