forked from centrifuge/go-substrate-rpc-client
/
extrinsic_retriever.go
174 lines (149 loc) · 5.26 KB
/
extrinsic_retriever.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package retriever
import (
"github.com/Cerebellum-Network/go-substrate-rpc-client/v9/registry"
"github.com/Cerebellum-Network/go-substrate-rpc-client/v9/registry/exec"
"github.com/Cerebellum-Network/go-substrate-rpc-client/v9/registry/parser"
"github.com/Cerebellum-Network/go-substrate-rpc-client/v9/rpc/chain/generic"
"github.com/Cerebellum-Network/go-substrate-rpc-client/v9/rpc/state"
"github.com/Cerebellum-Network/go-substrate-rpc-client/v9/types"
)
//nolint:lll
//go:generate mockery --name ExtrinsicRetriever --structname ExtrinsicRetrieverMock --filename extrinsic_retriever_mock.go --inpackage
// ExtrinsicRetriever is the interface used for retrieving and decoding extrinsic information.
//
// This interface is generic over types A, S, P, please check generic.GenericExtrinsicSignature for more
// information about these generic types.
type ExtrinsicRetriever[A, S, P any] interface {
GetExtrinsics(blockHash types.Hash) ([]*parser.Extrinsic[A, S, P], error)
}
// extrinsicRetriever implements the ExtrinsicRetriever interface.
type extrinsicRetriever[
A, S, P any,
B generic.GenericSignedBlock[A, S, P],
] struct {
extrinsicParser parser.ExtrinsicParser[A, S, P]
genericChain generic.Chain[A, S, P, B]
stateRPC state.State
registryFactory registry.Factory
chainExecutor exec.RetryableExecutor[B]
extrinsicParsingExecutor exec.RetryableExecutor[[]*parser.Extrinsic[A, S, P]]
callRegistry registry.CallRegistry
meta *types.Metadata
}
// NewExtrinsicRetriever creates a new ExtrinsicRetriever.
func NewExtrinsicRetriever[
A, S, P any,
B generic.GenericSignedBlock[A, S, P],
](
extrinsicParser parser.ExtrinsicParser[A, S, P],
genericChain generic.Chain[A, S, P, B],
stateRPC state.State,
registryFactory registry.Factory,
chainExecutor exec.RetryableExecutor[B],
extrinsicParsingExecutor exec.RetryableExecutor[[]*parser.Extrinsic[A, S, P]],
) (ExtrinsicRetriever[A, S, P], error) {
retriever := &extrinsicRetriever[A, S, P, B]{
extrinsicParser: extrinsicParser,
genericChain: genericChain,
stateRPC: stateRPC,
registryFactory: registryFactory,
chainExecutor: chainExecutor,
extrinsicParsingExecutor: extrinsicParsingExecutor,
}
if err := retriever.updateInternalState(nil); err != nil {
return nil, ErrInternalStateUpdate.Wrap(err)
}
return retriever, nil
}
// DefaultExtrinsicRetriever is the ExtrinsicRetriever interface with default for the generic types:
//
// Address - types.MultiAddress
// Signature - types.MultiSignature
// PaymentFields - generic.DefaultPaymentFields
type DefaultExtrinsicRetriever = ExtrinsicRetriever[
types.MultiAddress,
types.MultiSignature,
generic.DefaultPaymentFields,
]
// NewDefaultExtrinsicRetriever returns a DefaultExtrinsicRetriever with defaults for the generic types:
//
// Address - types.MultiAddress
// Signature - types.MultiSignature
// PaymentFields - generic.DefaultPaymentFields
// Block - *generic.DefaultGenericSignedBlock
//
// Note that these generic defaults also apply to the args.
func NewDefaultExtrinsicRetriever(
extrinsicParser parser.DefaultExtrinsicParser,
genericChain generic.DefaultChain,
stateRPC state.State,
registryFactory registry.Factory,
chainExecutor exec.RetryableExecutor[*generic.DefaultGenericSignedBlock],
extrinsicParsingExecutor exec.RetryableExecutor[[]*parser.DefaultExtrinsic],
) (DefaultExtrinsicRetriever, error) {
return NewExtrinsicRetriever[
types.MultiAddress,
types.MultiSignature,
generic.DefaultPaymentFields,
*generic.DefaultGenericSignedBlock,
](
extrinsicParser,
genericChain,
stateRPC,
registryFactory,
chainExecutor,
extrinsicParsingExecutor,
)
}
// GetExtrinsics retrieves a generic.SignedBlock and then parses the extrinsics found in it.
//
// Both the block retrieval and the extrinsic parsing are handled via the exec.RetryableExecutor
// in order to ensure retries in case of network errors or parsing errors due to an outdated call registry.
func (e *extrinsicRetriever[A, S, P, B]) GetExtrinsics(blockHash types.Hash) ([]*parser.Extrinsic[A, S, P], error) {
block, err := e.chainExecutor.ExecWithFallback(
func() (B, error) {
return e.genericChain.GetBlock(blockHash)
},
func() error {
return nil
},
)
if err != nil {
return nil, ErrBlockRetrieval.Wrap(err)
}
calls, err := e.extrinsicParsingExecutor.ExecWithFallback(
func() ([]*parser.Extrinsic[A, S, P], error) {
return e.extrinsicParser.ParseExtrinsics(e.callRegistry, block)
},
func() error {
return e.updateInternalState(&blockHash)
},
)
if err != nil {
return nil, ErrExtrinsicParsing.Wrap(err)
}
return calls, nil
}
// updateInternalState will retrieve the metadata at the provided blockHash, if provided,
// create a call registry based on this metadata and store both.
func (e *extrinsicRetriever[A, S, P, B]) updateInternalState(blockHash *types.Hash) error {
var (
meta *types.Metadata
err error
)
if blockHash == nil {
meta, err = e.stateRPC.GetMetadataLatest()
} else {
meta, err = e.stateRPC.GetMetadata(*blockHash)
}
if err != nil {
return ErrMetadataRetrieval.Wrap(err)
}
callRegistry, err := e.registryFactory.CreateCallRegistry(meta)
if err != nil {
return ErrCallRegistryCreation.Wrap(err)
}
e.meta = meta
e.callRegistry = callRegistry
return nil
}