-
Notifications
You must be signed in to change notification settings - Fork 5
/
runtime_decoder.go
155 lines (129 loc) · 4.22 KB
/
runtime_decoder.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
package types
import (
"bytes"
"errors"
"fmt"
sc "github.com/LimeChain/goscale"
"github.com/LimeChain/gosemble/primitives/log"
"github.com/LimeChain/gosemble/primitives/types"
primitives "github.com/LimeChain/gosemble/primitives/types"
)
var (
errInvalidExtrinsicVersion = errors.New("invalid Extrinsic version")
errInvalidLengthPrefix = errors.New("invalid length prefix")
)
type RuntimeDecoder interface {
DecodeBlock(buffer *bytes.Buffer) (primitives.Block, error)
DecodeUncheckedExtrinsic(buffer *bytes.Buffer) (primitives.UncheckedExtrinsic, error)
DecodeCall(buffer *bytes.Buffer) (primitives.Call, error)
}
type SudoDecoder interface {
DecodeSudoArgs(buffer *bytes.Buffer, decodeCallFunc func(buffer *bytes.Buffer) (primitives.Call, error)) (primitives.Call, error)
}
type runtimeDecoder struct {
modules []types.Module
extra primitives.SignedExtra
sudoIndex sc.U8 // Used for additional decoding related to Sudo calls. Default is 0 and not considered a valid sudo index.
logger log.WarnLogger
}
func NewRuntimeDecoder(modules []types.Module, extra primitives.SignedExtra, sudoIndex sc.U8, logger log.WarnLogger) RuntimeDecoder {
return runtimeDecoder{
modules: modules,
extra: extra,
sudoIndex: sudoIndex,
logger: logger,
}
}
func (rd runtimeDecoder) DecodeBlock(buffer *bytes.Buffer) (primitives.Block, error) {
header, err := primitives.DecodeHeader(buffer)
if err != nil {
return nil, err
}
compact, err := sc.DecodeCompact[sc.U128](buffer)
if err != nil {
return nil, err
}
length := compact.ToBigInt().Int64()
extrinsics := make([]types.UncheckedExtrinsic, length)
for i := 0; i < len(extrinsics); i++ {
extrinsic, err := rd.DecodeUncheckedExtrinsic(buffer)
if err != nil {
return nil, err
}
extrinsics[i] = extrinsic
}
return NewBlock(header, extrinsics), nil
}
func (rd runtimeDecoder) DecodeUncheckedExtrinsic(buffer *bytes.Buffer) (primitives.UncheckedExtrinsic, error) {
// This is a little more complicated than usual since the binary format must be compatible
// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
// will be a prefix of vector length.
expectedLenCompact, err := sc.DecodeCompact[sc.U64](buffer)
if err != nil {
return nil, err
}
expectedLength := expectedLenCompact.ToBigInt().Int64()
beforeLength := buffer.Len()
version, _ := buffer.ReadByte()
if version&ExtrinsicUnmaskVersion != ExtrinsicFormatVersion {
return nil, errInvalidExtrinsicVersion
}
extra := rd.extra.DeepCopy()
var extSignature sc.Option[primitives.ExtrinsicSignature]
isSigned := version&ExtrinsicBitSigned != 0
if isSigned {
sig, err := primitives.DecodeExtrinsicSignature(extra, buffer)
if err != nil {
return nil, err
}
extSignature = sc.NewOption[primitives.ExtrinsicSignature](sig)
}
// Decodes the dispatch call, including its arguments.
function, err := rd.DecodeCall(buffer)
if err != nil {
return nil, err
}
afterLength := buffer.Len()
if int(expectedLength) != beforeLength-afterLength {
return nil, errInvalidLengthPrefix
}
return NewUncheckedExtrinsic(sc.U8(version), extSignature, function, extra, rd.logger), nil
}
func (rd runtimeDecoder) DecodeCall(buffer *bytes.Buffer) (primitives.Call, error) {
moduleIndex, err := sc.DecodeU8(buffer)
if err != nil {
return nil, err
}
functionIndex, err := sc.DecodeU8(buffer)
if err != nil {
return nil, err
}
module, err := primitives.GetModule(moduleIndex, rd.modules)
if err != nil {
return nil, err
}
function, ok := module.Functions()[functionIndex]
if !ok {
return nil, fmt.Errorf("function index [%d] for module [%d] not found", functionIndex, moduleIndex)
}
if rd.isSudoCall(moduleIndex) {
sudoCall, ok := function.(SudoDecoder)
if !ok {
return nil, fmt.Errorf("function index [%d] for module [%d] does not implement sudo decoder", functionIndex, moduleIndex)
}
function, err = sudoCall.DecodeSudoArgs(buffer, rd.DecodeCall)
} else {
function, err = function.DecodeArgs(buffer)
}
if err != nil {
return nil, err
}
return function, nil
}
// Checks if the current module index is Sudo.
func (rd runtimeDecoder) isSudoCall(moduleIndex sc.U8) bool {
if rd.sudoIndex != 0 && moduleIndex == rd.sudoIndex {
return true
}
return false
}