@@ -10,6 +10,9 @@ import (
1010
1111 gsrpc "github.com/centrifuge/go-substrate-rpc-client/v4"
1212 "github.com/centrifuge/go-substrate-rpc-client/v4/client"
13+ "github.com/centrifuge/go-substrate-rpc-client/v4/registry"
14+ "github.com/centrifuge/go-substrate-rpc-client/v4/registry/parser"
15+ "github.com/centrifuge/go-substrate-rpc-client/v4/registry/retriever"
1316 "github.com/centrifuge/go-substrate-rpc-client/v4/signature"
1417 "github.com/centrifuge/go-substrate-rpc-client/v4/types"
1518 "github.com/centrifuge/go-substrate-rpc-client/v4/types/codec"
@@ -50,15 +53,6 @@ type ExtrinsicInfo struct {
5053 Hash types.Hash
5154 BlockHash types.Hash
5255 Index uint // index number of extrinsic in a block
53-
54- // EventsRaw contains all the events in the given block
55- // if you want to filter events for an extrinsic, use the Index
56- EventsRaw types.EventRecordsRaw
57- }
58-
59- // Events returns all the events occurred in a given block
60- func (e ExtrinsicInfo ) Events (meta * types.Metadata ) (events Events , err error ) {
61- return events , e .EventsRaw .DecodeEventRecords (meta , & events )
6256}
6357
6458//go:generate mockery --name API --structname APIMock --filename api_mock.go --inpackage
@@ -162,10 +156,11 @@ func (dsa *defaultSubstrateAPI) GetPendingExtrinsics() ([]types.Extrinsic, error
162156}
163157
164158type api struct {
165- sapi substrateAPI
166- dispatcher jobs.Dispatcher
167- accounts map [string ]uint32
168- accMu sync.Mutex // accMu to protect accounts
159+ sapi substrateAPI
160+ dispatcher jobs.Dispatcher
161+ accounts map [string ]uint32
162+ accMu sync.Mutex
163+ eventRetriever retriever.EventRetriever
169164
170165 centChainMaxRetries int
171166 centChainRetryInterval time.Duration
@@ -177,6 +172,7 @@ func NewAPI(
177172 dispatcher jobs.Dispatcher ,
178173 centChainMaxRetries int ,
179174 centChainRetryInterval time.Duration ,
175+ eventRetriever retriever.EventRetriever ,
180176) API {
181177 return & api {
182178 sapi : sapi ,
@@ -185,6 +181,7 @@ func NewAPI(
185181 accMu : sync.Mutex {},
186182 centChainMaxRetries : centChainMaxRetries ,
187183 centChainRetryInterval : centChainRetryInterval ,
184+ eventRetriever : eventRetriever ,
188185 }
189186}
190187
@@ -384,8 +381,7 @@ func (a *api) getDispatcherRunnerFunc(
384381
385382 log .Debugf ("Extrinsic %s found in block %d" , txHash .Hex (), * blockNumber )
386383
387- eventsRaw , err := a .checkExtrinsicEventSuccess (meta , bh , extIdx )
388- if err != nil {
384+ if err := a .checkExtrinsicEventSuccess (meta , bh , extIdx ); err != nil {
389385 log .Errorf ("Couldn't check extrinsic event success in block %d: %s" , * blockNumber , err )
390386
391387 return nil , err
@@ -395,7 +391,6 @@ func (a *api) getDispatcherRunnerFunc(
395391 Hash : txHash ,
396392 BlockHash : bh ,
397393 Index : uint (extIdx ),
398- EventsRaw : eventsRaw ,
399394 }
400395
401396 return info , nil
@@ -404,73 +399,157 @@ func (a *api) getDispatcherRunnerFunc(
404399 return fn
405400}
406401
402+ const (
403+ ExtrinsicSuccessEventName = "System.ExtrinsicSuccess"
404+ ExtrinsicFailedEventName = "System.ExtrinsicFailed"
405+ DispatchErrorFieldName = "sp_runtime.DispatchError.dispatch_error"
406+ )
407+
407408func (a * api ) checkExtrinsicEventSuccess (
408409 meta * types.Metadata ,
409410 blockHash types.Hash ,
410411 extrinsicIdx int ,
411- ) (eventsRaw types.EventRecordsRaw , err error ) {
412- key , err := types .CreateStorageKey (meta , "System" , "Events" )
412+ ) error {
413+ events , err := a .eventRetriever .GetEvents (blockHash )
414+
413415 if err != nil {
414- return nil , err
416+ return fmt . Errorf ( "event retrieval error: %w" , err )
415417 }
416418
417- err = a .sapi .GetStorage (key , & eventsRaw , blockHash )
419+ for _ , event := range events {
420+ switch {
421+ case event .Name == ExtrinsicSuccessEventName &&
422+ event .Phase .IsApplyExtrinsic &&
423+ event .Phase .AsApplyExtrinsic == uint32 (extrinsicIdx ):
424+ if err := checkSuccessfulProxyExecution (meta , events , extrinsicIdx ); err != nil {
425+ return fmt .Errorf ("proxy call was not successful: %w" , err )
426+ }
427+
428+ return nil
429+ case event .Name == ExtrinsicFailedEventName &&
430+ event .Phase .IsApplyExtrinsic &&
431+ event .Phase .AsApplyExtrinsic == uint32 (extrinsicIdx ):
432+ errorID , err := registry .ProcessDecodedFieldValue [* registry.ErrorID ](
433+ event .Fields ,
434+ func (fieldIndex int , field * registry.DecodedField ) bool {
435+ return field .Name == DispatchErrorFieldName
436+ },
437+ getErrorIDFromDispatchError ,
438+ )
439+
440+ if err != nil {
441+ return fmt .Errorf ("extrinsic with index %d failed" , extrinsicIdx )
442+ }
443+
444+ return getMetaError (meta , errorID )
445+ }
446+ }
447+
448+ return errors .New ("should not have reached this step: %v" , events )
449+ }
450+
451+ func getMetaError (meta * types.Metadata , errorID * registry.ErrorID ) error {
452+ metaErr , err := meta .FindError (errorID .ModuleIndex , errorID .ErrorIndex )
453+
418454 if err != nil {
419- return nil , err
455+ return fmt .Errorf ("extrinsic failed" )
456+ }
457+
458+ return errors .New (
459+ "extrinsic failed with '%s - %s'" ,
460+ metaErr .Name ,
461+ metaErr .Value ,
462+ )
463+ }
464+
465+ func getErrorIDFromDispatchError (value any ) (* registry.ErrorID , error ) {
466+ dispatchErrorFields , ok := value .(registry.DecodedFields )
467+
468+ if ! ok {
469+ return nil , fmt .Errorf ("expected dispatch error field to be a slice of decoded fields" )
420470 }
421471
422- events := Events {}
423- err = eventsRaw .DecodeEventRecords (meta , & events )
472+ if len (dispatchErrorFields ) != 1 {
473+ return nil , fmt .Errorf ("expected dispatch error to have one field" )
474+ }
475+
476+ moduleErrorFields , ok := dispatchErrorFields [0 ].Value .(registry.DecodedFields )
477+
478+ if ! ok {
479+ return nil , fmt .Errorf ("expected module error fields to be a slice of decoded fields" )
480+ }
481+
482+ moduleIndex , err := registry .GetDecodedFieldAsType [types.U8 ](
483+ moduleErrorFields ,
484+ func (fieldIndex int , field * registry.DecodedField ) bool {
485+ return field .Name == "index"
486+ },
487+ )
488+
424489 if err != nil {
425- return nil , err
490+ return nil , fmt . Errorf ( "module index retrieval: %w" , err )
426491 }
427492
428- // Check success events
429- for _ , es := range events .System_ExtrinsicSuccess {
430- if es .Phase .IsApplyExtrinsic && es .Phase .AsApplyExtrinsic == uint32 (extrinsicIdx ) {
431- // Extra check for proxy calls.
432- if err := checkSuccessfulProxyExecution (events , meta , extrinsicIdx ); err != nil {
433- return nil , errors .New ("proxy call was not successful: %s" , err )
434- }
493+ errorIndex , err := registry .GetDecodedFieldAsSliceOfType [types.U8 ](
494+ moduleErrorFields ,
495+ func (fieldIndex int , field * registry.DecodedField ) bool {
496+ return field .Name == "error"
497+ },
498+ )
435499
436- return eventsRaw , nil
437- }
500+ if err != nil {
501+ return nil , fmt . Errorf ( "error index retrieval: %w" , err )
438502 }
439503
440- // Otherwise, check failure events
441- for _ , es := range events .System_ExtrinsicFailed {
442- if es .Phase .IsApplyExtrinsic && es .Phase .AsApplyExtrinsic == uint32 (extrinsicIdx ) {
443- return nil , handleDispatchError (meta , es .DispatchError , extrinsicIdx )
444- }
504+ if len (errorIndex ) != 4 {
505+ return nil , fmt .Errorf ("unexpected error index length" )
445506 }
446507
447- return nil , errors .New ("should not have reached this step: %v" , events )
448- }
508+ var errorIndexArray [4 ]types.U8
449509
450- func handleDispatchError (meta * types.Metadata , dispatchError types.DispatchError , extrinsicIdx int ) error {
451- if dispatchError .IsModule {
452- moduleErr := dispatchError .ModuleError
453- if metaErr , findErr := meta .FindError (moduleErr .Index , moduleErr .Error ); findErr == nil {
454- return errors .New (
455- "extrinsic %d failed with '%s - %s'" ,
456- extrinsicIdx ,
457- metaErr .Name ,
458- metaErr .Value ,
459- )
460- }
510+ for i , item := range errorIndex {
511+ errorIndexArray [i ] = item
461512 }
462513
463- return errors .New ("extrinsic %d failed: %v" , extrinsicIdx , dispatchError )
514+ return & registry.ErrorID {
515+ ModuleIndex : moduleIndex ,
516+ ErrorIndex : errorIndexArray ,
517+ }, nil
464518}
465519
466- func checkSuccessfulProxyExecution (events Events , meta * types.Metadata , extrinsicIdx int ) error {
467- for _ , event := range events .Proxy_ProxyExecuted {
468- if event .Phase .IsApplyExtrinsic && event .Phase .AsApplyExtrinsic == uint32 (extrinsicIdx ) {
469- if ! event .Result .Ok {
470- return handleDispatchError (meta , event .Result .Error , extrinsicIdx )
520+ const (
521+ ProxyExecutedEventName = "Proxy.ProxyExecuted"
522+ ResultFieldName = "Result.result"
523+ ProxyExecutedExpectedLookupIndex = 40
524+ )
525+
526+ func checkSuccessfulProxyExecution (meta * types.Metadata , events []* parser.Event , extrinsicIdx int ) error {
527+ for _ , event := range events {
528+ if event .Name == ProxyExecutedEventName && event .Phase .IsApplyExtrinsic && event .Phase .AsApplyExtrinsic == uint32 (extrinsicIdx ) {
529+ res , err := registry .GetDecodedFieldAsType [registry.DecodedFields ](event .Fields , func (fieldIndex int , field * registry.DecodedField ) bool {
530+ return field .Name == ResultFieldName
531+ })
532+
533+ if err != nil {
534+ return fmt .Errorf ("result field retrieval: %w" , err )
471535 }
472536
473- return nil
537+ if len (res ) != 1 {
538+ return errors .New ("result field has unexpected size" )
539+ }
540+
541+ if res [0 ].Value == nil && res [0 ].LookupIndex == ProxyExecutedExpectedLookupIndex {
542+ // The DispatchResult is Ok(()).
543+ return nil
544+ }
545+
546+ errorID , err := getErrorIDFromDispatchError (res [0 ].Value )
547+
548+ if err != nil {
549+ return errors .New ("proxy execution was unsuccessful" )
550+ }
551+
552+ return getMetaError (meta , errorID )
474553 }
475554 }
476555
0 commit comments