Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adopt the GSRPC changes for EventRetriever #36

Draft
wants to merge 11 commits into
base: fix/check-metadata-hash
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 35 additions & 47 deletions chains/substrate/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import (
"bytes"
"context"
"fmt"
"github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever"
"github.com/centrifuge/go-substrate-rpc-client/v4/scale"
"github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic"
"github.com/centrifuge/go-substrate-rpc-client/v4/types/extrinsic/extensions"
"math/big"
"strings"
"sync"
"time"

"github.com/centrifuge/go-substrate-rpc-client/v4/rpc/author"
"github.com/centrifuge/go-substrate-rpc-client/v4/scale"
"github.com/centrifuge/go-substrate-rpc-client/v4/signature"
"github.com/centrifuge/go-substrate-rpc-client/v4/types"
"github.com/rs/zerolog/log"
Expand All @@ -23,20 +25,22 @@ import (
)

type SubstrateClient struct {
key *signature.KeyringPair // Keyring used for signing
nonceLock sync.Mutex // Locks nonce for updates
nonce types.U32 // Latest account nonce
tip uint64
Conn *connection.Connection
ChainID *big.Int
key *signature.KeyringPair // Keyring used for signing
nonceLock sync.Mutex // Locks nonce for updates
nonce types.U32 // Latest account nonce
tip uint64
Conn *connection.Connection
eventRetriever retriever.EventRetriever
ChainID *big.Int
}

func NewSubstrateClient(conn *connection.Connection, key *signature.KeyringPair, chainID *big.Int, tip uint64) *SubstrateClient {
func NewSubstrateClient(conn *connection.Connection, key *signature.KeyringPair, chainID *big.Int, tip uint64, eventRetriever retriever.EventRetriever) *SubstrateClient {
return &SubstrateClient{
key: key,
Conn: conn,
ChainID: chainID,
tip: tip,
key: key,
Conn: conn,
ChainID: chainID,
tip: tip,
eventRetriever: eventRetriever,
}
}

Expand All @@ -56,7 +60,7 @@ func (c *SubstrateClient) Transact(method string, args ...interface{}) (types.Ha
return types.Hash{}, nil, fmt.Errorf("failed to construct call: %w", err)
}

ext := extrinsic.NewDynamicExtrinsic(&call)
ext := extrinsic.NewExtrinsic(call)

// Get latest runtime version
rv, err := c.Conn.RPC.State.GetRuntimeVersionLatest()
Expand Down Expand Up @@ -87,8 +91,7 @@ func (c *SubstrateClient) Transact(method string, args ...interface{}) (types.Ha
if err != nil {
return types.Hash{}, nil, fmt.Errorf("submission of extrinsic failed: %w", err)
}

hash, err := DynamicExtrinsicHash(ext)
hash, err := ExtrinsicHash(ext)
if err != nil {
return types.Hash{}, nil, err
}
Expand All @@ -99,7 +102,7 @@ func (c *SubstrateClient) Transact(method string, args ...interface{}) (types.Ha
return hash, sub, nil
}

func (c *SubstrateClient) TrackExtrinsic(extHash types.Hash, sub *author.ExtrinsicStatusSubscription) error {
func (c *SubstrateClient) TrackExtrinsic(extHash types.Hash, sub *author.ExtrinsicStatusSubscription, extrinsicMethod string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Minute*10))
defer sub.Unsubscribe()
defer cancel()
Expand All @@ -113,7 +116,7 @@ func (c *SubstrateClient) TrackExtrinsic(extHash types.Hash, sub *author.Extrins
}
if status.IsFinalized {
log.Info().Str("extrinsic", extHash.Hex()).Msgf("Extrinsic is finalized in block with hash: %#x", status.AsFinalized)
return c.checkExtrinsicSuccess(extHash, status.AsFinalized)
return c.checkExtrinsicSuccess(extHash, status.AsFinalized, extrinsicMethod)
}
}
case <-ctx.Done():
Expand Down Expand Up @@ -148,40 +151,31 @@ func (c *SubstrateClient) nextNonce(meta *types.Metadata) (types.U32, error) {
return latestNonce, nil
}

func (c *SubstrateClient) submitAndWatchExtrinsic(meta *types.Metadata, ext extrinsic.DynamicExtrinsic, opts ...extrinsic.SigningOption) (*author.ExtrinsicStatusSubscription, error) {
func (c *SubstrateClient) submitAndWatchExtrinsic(meta *types.Metadata, ext extrinsic.Extrinsic, opts ...extrinsic.SigningOption) (*author.ExtrinsicStatusSubscription, error) {
err := ext.Sign(*c.key, meta, opts...)
if err != nil {
return nil, err
}

sub, err := c.Conn.RPC.Author.SubmitAndWatchDynamicExtrinsic(ext)
sub, err := c.Conn.RPC.Author.SubmitAndWatchExtrinsic(ext)
if err != nil {
return nil, err
}

return sub, nil
}

func (c *SubstrateClient) checkExtrinsicSuccess(extHash types.Hash, blockHash types.Hash) error {
block, err := c.Conn.Chain.GetBlock(blockHash)
if err != nil {
return err
}

evts, err := c.Conn.GetBlockEvents(blockHash)
func (c *SubstrateClient) checkExtrinsicSuccess(extHash types.Hash, blockHash types.Hash, extrinsicMethod string) error {
blockEvents, err := c.eventRetriever.GetEvents(blockHash)
if err != nil {
return err
}

for _, event := range evts {
index := event.Phase.AsApplyExtrinsic
hash, err := ExtrinsicHash(block.Block.Extrinsics[index])
if err != nil {
return err
}

if extHash != hash {
continue
extrinsicCallCounter := 0
extrinsicSuccessCounter := 0
for _, event := range blockEvents {
if strings.EqualFold(event.Name, extrinsicMethod) {
extrinsicCallCounter++
}

if event.Name == events.ExtrinsicFailedEvent {
Expand All @@ -191,10 +185,14 @@ func (c *SubstrateClient) checkExtrinsicSuccess(extHash types.Hash, blockHash ty
return fmt.Errorf("extrinsic failed with failed handler execution")
}
if event.Name == events.ExtrinsicSuccessEvent {
return nil
extrinsicSuccessCounter++
}
}

if extrinsicCallCounter == 1 && extrinsicSuccessCounter >= 1 {
return nil
}

return fmt.Errorf("no event found")
}

Expand All @@ -206,17 +204,7 @@ func (c *SubstrateClient) LatestBlock() (*big.Int, error) {
return big.NewInt(int64(block.Block.Header.Number)), nil
}

func ExtrinsicHash(ext types.Extrinsic) (types.Hash, error) {
extHash := bytes.NewBuffer([]byte{})
encoder := scale.NewEncoder(extHash)
err := ext.Encode(*encoder)
if err != nil {
return types.Hash{}, err
}
return types.NewHash(extHash.Bytes()), nil
}

func DynamicExtrinsicHash(ext extrinsic.DynamicExtrinsic) (types.Hash, error) {
func ExtrinsicHash(ext extrinsic.Extrinsic) (types.Hash, error) {
extHash := bytes.NewBuffer([]byte{})
encoder := scale.NewEncoder(extHash)
err := ext.Encode(*encoder)
Expand Down
3 changes: 2 additions & 1 deletion chains/substrate/listener/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package listener

import (
"context"
"github.com/centrifuge/go-substrate-rpc-client/v4/types/block"
"math/big"
"time"

Expand All @@ -19,7 +20,7 @@ type EventHandler interface {

type ChainConnection interface {
GetFinalizedHead() (types.Hash, error)
GetBlock(blockHash types.Hash) (*types.SignedBlock, error)
GetBlock(blockHash types.Hash) (*block.SignedBlock, error)
}

type BlockStorer interface {
Expand Down
37 changes: 19 additions & 18 deletions chains/substrate/listener/listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package listener_test
import (
"context"
"fmt"
"github.com/centrifuge/go-substrate-rpc-client/v4/types/block"
"math/big"
"testing"
"time"
Expand Down Expand Up @@ -69,8 +70,8 @@ func (s *ListenerTestSuite) Test_ListenToEvents_RetriesIfBlockUnavailable() {

func (s *ListenerTestSuite) Test_ListenToEvents_SleepsIfBlockTooNew() {
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: 104,
},
Expand All @@ -91,8 +92,8 @@ func (s *ListenerTestSuite) Test_ListenToEvents_RetriesInCaseOfHandlerFailure()

// First pass
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: types.BlockNumber(head.Int64()),
},
Expand All @@ -102,8 +103,8 @@ func (s *ListenerTestSuite) Test_ListenToEvents_RetriesInCaseOfHandlerFailure()
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(fmt.Errorf("error"))
// Second pass
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: types.BlockNumber(head.Int64()),
},
Expand All @@ -115,8 +116,8 @@ func (s *ListenerTestSuite) Test_ListenToEvents_RetriesInCaseOfHandlerFailure()
s.mockBlockStorer.EXPECT().StoreBlock(endBlock, s.domainID).Return(nil)
// third pass
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: 100,
},
Expand All @@ -138,8 +139,8 @@ func (s *ListenerTestSuite) Test_ListenToEvents_IgnoresBlockStorerError() {

// First pass
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: types.BlockNumber(head.Int64()),
},
Expand All @@ -151,8 +152,8 @@ func (s *ListenerTestSuite) Test_ListenToEvents_IgnoresBlockStorerError() {
s.mockBlockStorer.EXPECT().StoreBlock(endBlock, s.domainID).Return(fmt.Errorf("error"))
// second pass
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: 95,
},
Expand All @@ -173,24 +174,24 @@ func (s *ListenerTestSuite) Test_ListenToEvents_UsesHeadAsStartBlockIfNilPassed(
newHead := big.NewInt(120)

s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: types.BlockNumber(oldHead.Int64()),
},
},
}, nil)
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: types.BlockNumber(newHead.Int64()),
},
},
}, nil)
s.mockClient.EXPECT().GetFinalizedHead().Return(types.Hash{}, nil)
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&types.SignedBlock{
Block: types.Block{
s.mockClient.EXPECT().GetBlock(gomock.Any()).Return(&block.SignedBlock{
Block: block.Block{
Header: types.Header{
Number: types.BlockNumber(95),
},
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.21
toolchain go1.22.2

require (
github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.2-0.20240724202640-8bafff8c25ea
github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.2-0.20240917092335-6cd32ee625ca
github.com/ethereum/go-ethereum v1.13.2
github.com/imdario/mergo v0.3.12
github.com/pkg/errors v0.9.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlH
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.2-0.20240724202640-8bafff8c25ea h1:O2DsP9UvPHFXQVt5W/+i7H+KegncyVKEyO6tkZ9klUs=
github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.2-0.20240724202640-8bafff8c25ea/go.mod h1:k61SBXqYmnZO4frAJyH3iuqjolYrYsq79r8EstmklDY=
github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.2-0.20240917092335-6cd32ee625ca h1:fOZsrGNsIDZjHSAqUbtHG5T90HGZFe1TogncYKmr3+4=
github.com/centrifuge/go-substrate-rpc-client/v4 v4.2.2-0.20240917092335-6cd32ee625ca/go.mod h1:k61SBXqYmnZO4frAJyH3iuqjolYrYsq79r8EstmklDY=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
Expand Down
5 changes: 3 additions & 2 deletions mock/substrateListener.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading