dev
dart 101 lines 3.16 KB
Raw
1 import 'dart:typed_data';
2
3 import 'package:web3dart/crypto.dart';
4
5 /// RLP Decode
6 ///
7 /// Adapted from https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/rlp
8
9 class _Decoded {
10 Uint8List remainder;
11 List data;
12
13 _Decoded(this.data, this.remainder);
14 }
15
16 int _decodeLength(Uint8List v) {
17 if (v[0] == 0) throw Exception('invalid RLP: extra zeros');
18 return int.parse(bytesToHex(v), radix: 16);
19 }
20
21 _Decoded _decode(Uint8List input) {
22 final firstByte = input[0];
23
24 if (firstByte <= 0x7f) {
25 // a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding.
26 return _Decoded(input.sublist(0, 1), input.sublist(1));
27 } else if (firstByte <= 0xb7) {
28 // string is 0-55 bytes long. A single byte with value 0x80 plus the length of the string followed by the string
29 // The range of the first byte is [0x80, 0xb7]
30 final length = firstByte - 0x7f;
31
32 // set 0x80 null to 0
33 final data = firstByte == 0x80 ? Uint8List(0) : input.sublist(1, length);
34
35 if (length == 2 && data[0] < 0x80) {
36 throw Exception('invalid RLP encoding: invalid prefix, single byte < 0x80 are not prefixed');
37 }
38
39 return _Decoded(data, input.sublist(length));
40 } else if (firstByte <= 0xbf) {
41 // string is greater than 55 bytes long. A single byte with the value (0xb7 plus the length of the length),
42 // followed by the length, followed by the string
43 final lLength = firstByte - 0xb6;
44 if (input.length - 1 < lLength) {
45 throw Exception('invalid RLP: not enough bytes for string length');
46 }
47
48 final length = _decodeLength(input.sublist(1, lLength));
49 if (length <= 55) {
50 throw Exception('invalid RLP: expected string length to be greater than 55');
51 }
52
53 final data = input.sublist(lLength, length + lLength);
54 return _Decoded(data, input.sublist(length + lLength));
55 } else if (firstByte <= 0xf7) {
56 // a list between 0-55 bytes long
57 final length = firstByte - 0xbf;
58 var innerRemainder = input.sublist(1, length);
59
60 final decoded = [];
61 while (innerRemainder.isNotEmpty) {
62 final d = _decode(innerRemainder);
63 decoded.add(d.data);
64 innerRemainder = d.remainder;
65 }
66
67 return _Decoded(decoded, input.sublist(length));
68 } else {
69 // a list over 55 bytes long
70 final lLength = firstByte - 0xf6;
71
72 final length = _decodeLength(input.sublist(1, lLength));
73 if (length < 56) {
74 throw Exception('invalid RLP: encoded list too short');
75 }
76
77 final totalLength = lLength + length;
78 if (totalLength > input.length) {
79 throw Exception('invalid RLP: total length is larger than the data');
80 }
81
82 var innerRemainder = input.sublist(lLength, totalLength);
83 final decoded = [];
84 while (innerRemainder.isNotEmpty) {
85 final d = _decode(innerRemainder);
86 decoded.add(d.data);
87 innerRemainder = d.remainder;
88 }
89
90 return _Decoded(decoded, input.sublist(totalLength));
91 }
92 }
93
94 /// RLP Decoding based on https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
95 List decode(Uint8List input) {
96 final decoded = _decode(input);
97
98 if (decoded.remainder.isNotEmpty) throw Exception('invalid RLP: remainder must be zero');
99
100 return decoded.data;
101 }