Skip to content

Commit d784fab

Browse files
author
Arjan Zijderveld
committed
Added encoding of extrinsics
1 parent 4a22ff8 commit d784fab

File tree

5 files changed

+105
-8
lines changed

5 files changed

+105
-8
lines changed

scalecodec/base.py

+7
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ def __str__(self):
290290
return str(self.value) or ''
291291

292292
def encode(self, value):
293+
self.value = value
293294
self.data = self.process_encode(value)
294295
return self.data
295296

@@ -298,6 +299,12 @@ def process_encode(self, value):
298299

299300
@classmethod
300301
def get_decoder_class(cls, type_string, data=None, **kwargs):
302+
"""
303+
:param type_string:
304+
:param data:
305+
:param kwargs:
306+
:return: ScaleType
307+
"""
301308

302309
type_parts = None
303310

scalecodec/block.py

+38-7
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def process(self):
112112

113113
self.era = self.process_type('Era')
114114

115-
self.nonce = self.process_type('Compact<U64>')
115+
self.nonce = self.process_type('Compact<Index>')
116116

117117
self.tip = self.process_type('Compact<Balance>')
118118

@@ -129,7 +129,7 @@ def process(self):
129129

130130
self.era = self.process_type('Era')
131131

132-
self.nonce = self.process_type('Compact<U64>')
132+
self.nonce = self.process_type('Compact<Index>')
133133

134134
self.tip = self.process_type('Compact<Balance>')
135135

@@ -148,7 +148,7 @@ def process(self):
148148

149149
self.era = self.process_type('Era')
150150

151-
self.nonce = self.process_type('Compact<U64>')
151+
self.nonce = self.process_type('Compact<Index>')
152152

153153
self.tip = self.process_type('Compact<Balance>')
154154

@@ -195,6 +195,7 @@ def process(self):
195195
result['account_id'] = self.address.account_id
196196
result['account_index'] = self.address.account_index
197197
result['account_idx'] = self.address.account_idx
198+
result['signature_version'] = self.signature_version.value
198199
result['signature'] = self.signature.value.replace('0x', '')
199200
result['extrinsic_hash'] = self.extrinsic_hash
200201
if self.call_index:
@@ -216,14 +217,14 @@ def process(self):
216217
return result
217218

218219
def process_encode(self, value):
219-
# Check requirements
220+
220221
if 'call_index' in value:
221222
self.call_index = value['call_index']
222223

223224
elif 'call_module' in value and 'call_function' in value:
224225
# Look up call module from metadata
225226
for call_index, (call_module, call) in self.metadata.call_index.items():
226-
if call_module.name == value['call_module'] and call.name == value['call_function']:
227+
if call_module.name.lower() == value['call_module'].lower() and call.name == value['call_function']:
227228
self.call_index = call_index
228229
self.call_module = call_module
229230
self.call = call
@@ -235,14 +236,44 @@ def process_encode(self, value):
235236
elif not self.call_module or not self.call:
236237
raise ValueError('No call module and function specified')
237238

239+
# Determine version (Fixed to V4 for now)
240+
if 'account_id' in value:
241+
self.version_info = '84'
242+
self.contains_transaction = True
243+
else:
244+
self.version_info = '04'
245+
self.contains_transaction = False
246+
238247
if self.contains_transaction:
239248
data = ScaleBytes('0x84')
240-
raise NotImplementedError('Encoding of signed extrinsics not supported')
249+
250+
self.address = self.get_decoder_class('Address', metadata=self.metadata)
251+
data += self.address.encode(value['account_id'])
252+
253+
self.signature_version = self.get_decoder_class('U8', metadata=self.metadata)
254+
data += self.signature_version.encode(value['signature_version'])
255+
256+
self.signature = self.get_decoder_class('Signature', metadata=self.metadata)
257+
data += self.signature.encode('0x{}'.format(value['signature'].replace('0x', '')))
258+
259+
self.era = self.get_decoder_class('Era', metadata=self.metadata)
260+
data += self.era.encode(value['era'])
261+
262+
self.nonce = self.get_decoder_class('Compact<Index>', metadata=self.metadata)
263+
data += self.nonce.encode(value['nonce'])
264+
265+
self.tip = self.get_decoder_class('Compact<Balance>', metadata=self.metadata)
266+
data += self.tip.encode(value['tip'])
267+
241268
else:
242269
data = ScaleBytes('0x04')
243270

244271
data += ScaleBytes(bytearray.fromhex(self.call_index))
245272

273+
# Convert params to call_args TODO refactor
274+
if not value.get('call_args') and value.get('params'):
275+
value['call_args'] = {call_arg['name']: call_arg['value'] for call_arg in value.get('params')}
276+
246277
# Encode call params
247278
if len(self.call.args) > 0:
248279
for arg in self.call.args:
@@ -254,7 +285,7 @@ def process_encode(self, value):
254285
arg_obj = self.get_decoder_class(arg.type, metadata=self.metadata)
255286
data += arg_obj.encode(param_value)
256287

257-
# Wrap payload with een length Compact<u32>
288+
# Wrap payload with a length Compact<u32>
258289
length_obj = self.get_decoder_class('Compact<u32>')
259290
data = length_obj.encode(data.length) + data
260291

scalecodec/type_registry/default.json

+13-1
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,19 @@
267267
]
268268
},
269269
"EventIndex": "u32",
270-
"Extrinsic": "ExtrinsicDecoder",
270+
"Extrinsic": "ExtrinsicsDecoder",
271+
"ExtrinsicPayloadValue": {
272+
"type": "struct",
273+
"type_mapping": [
274+
["call", "CallBytes"],
275+
["era", "Era"],
276+
["nonce", "Compact<Index>"],
277+
["tip", "Compact<Balance>"],
278+
["specVersion", "u32"],
279+
["genesisHash", "Hash"],
280+
["blockHash", "Hash"]
281+
]
282+
},
271283
"Gas": "u64",
272284
"IdentityFields": {
273285
"type": "set",

scalecodec/types.py

+15
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,15 @@ def process_encode(self, value):
225225
return data
226226

227227

228+
class CallBytes(ScaleType):
229+
230+
def process(self):
231+
raise NotImplementedError()
232+
233+
def process_encode(self, value):
234+
return bytes.fromhex(value[2:])
235+
236+
228237
class U8(ScaleType):
229238

230239
def process(self):
@@ -625,6 +634,12 @@ def process(self):
625634
else:
626635
return option_byte + self.get_next_bytes(1).hex()
627636

637+
def process_encode(self, value):
638+
if value == '00':
639+
return ScaleBytes('0x00')
640+
else:
641+
raise NotImplementedError('Mortal Era not implemented')
642+
628643

629644
class EraIndex(U32):
630645
pass

test/test_extrinsic_payload.py

+32
Original file line numberDiff line numberDiff line change
@@ -2140,3 +2140,35 @@ def test_encode_utility_cancel_as_multi_payload(self):
21402140
})
21412141

21422142
self.assertEqual(str(payload), "0xb804180405000010270000010000000123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
2143+
2144+
def test_signed_extrinsic(self):
2145+
extrinsic = ExtrinsicsDecoder(metadata=self.metadata_decoder)
2146+
2147+
extrinsic_value = {
2148+
'account_id': '5E9oDs9PjpsBbxXxRE9uMaZZhnBAV38n2ouLB28oecBDdeQo',
2149+
'signature_version': 1,
2150+
'signature': '728b4057661816aa24918219ff90d10a34f1db4e81494d23c83ef54991980f77cf901acd970cb36d3c9c9e166d27a83a3aee648d4085e2bdb9e7622c0538e381',
2151+
'call_function': 'transfer',
2152+
'call_module': 'balances',
2153+
'nonce': 0,
2154+
'era': '00',
2155+
'tip': 0,
2156+
'params': [
2157+
{'name': 'dest', 'type': 'Address',
2158+
'value': '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'},
2159+
{'name': 'value', 'type': 'Compact<Balance>', 'value': 1000000000000}
2160+
]
2161+
}
2162+
2163+
extrinsic_hex = extrinsic.encode(extrinsic_value)
2164+
2165+
obj = ScaleDecoder.get_decoder_class(
2166+
"ExtrinsicsDecoder",
2167+
data=extrinsic_hex,
2168+
metadata=self.metadata_decoder
2169+
)
2170+
2171+
decoded_extrinsic = obj.decode()
2172+
2173+
self.assertEqual(extrinsic_value['signature'], decoded_extrinsic['signature'])
2174+
self.assertEqual(extrinsic_value['params'][0]['value'], decoded_extrinsic['params'][0]['value'])

0 commit comments

Comments
 (0)