Skip to content

Commit ae01eab

Browse files
authored
api: Use EventRetriever when parsing events (#1603)
* api: Use EventRetriever when parsing events * api: Register DecodedFields * deps: Update go-substrate-rpc-client to v4.1.0 * deps: Update chain-custom-types to v1.0.8 * test-client: Use EventRetriever * loans: Update CreatdLoan retrieval test * testworld: Fix investor test * api: Remove events from ExtrinsicInfo
1 parent 32fc52e commit ae01eab

11 files changed

Lines changed: 577 additions & 387 deletions

File tree

centchain/api.go

Lines changed: 137 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -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

164158
type 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+
407408
func (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

centchain/api_integration_test.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ func TestApi_GetBlockLatest(t *testing.T) {
112112
assert.NotNil(t, block)
113113
}
114114

115-
func TestApi_SubmitAndWatch(t *testing.T) {
115+
func TestApi_SubmitAndWatch_ExtrinsicSuccess(t *testing.T) {
116116
meta, err := testAPI.GetMetadataLatest()
117117
assert.NoError(t, err)
118118

@@ -129,12 +129,8 @@ func TestApi_SubmitAndWatch(t *testing.T) {
129129
podOperator, err := cfgSrv.GetPodOperator()
130130
assert.NoError(t, err)
131131

132-
info, err := testAPI.SubmitAndWatch(ctx, meta, call, podOperator.ToKeyringPair())
132+
_, err = testAPI.SubmitAndWatch(ctx, meta, call, podOperator.ToKeyringPair())
133133
assert.NoError(t, err)
134-
135-
events, err := info.Events(meta)
136-
assert.NoError(t, err)
137-
assert.True(t, len(events.System_ExtrinsicSuccess) > 1)
138134
}
139135

140136
func TestApi_GetPendingExtrinsics(t *testing.T) {

0 commit comments

Comments
 (0)