ripple-binary-codec 1.7.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/enums/definitions.json +250 -1
- package/dist/enums/src/enums/definitions.json +250 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/issue.d.ts +39 -0
- package/dist/types/issue.js +81 -0
- package/dist/types/issue.js.map +1 -0
- package/dist/types/xchain-bridge.d.ts +45 -0
- package/dist/types/xchain-bridge.js +102 -0
- package/dist/types/xchain-bridge.js.map +1 -0
- package/package.json +3 -4
- package/src/README.md +3 -0
- package/src/binary.ts +188 -0
- package/src/coretypes.ts +31 -0
- package/src/enums/README.md +144 -0
- package/src/enums/bytes.ts +75 -0
- package/src/enums/constants.ts +4 -0
- package/src/enums/definitions.json +2599 -0
- package/src/enums/field.ts +85 -0
- package/src/enums/index.ts +34 -0
- package/src/enums/utils-renumber.ts +134 -0
- package/src/enums/xrpl-definitions-base.ts +111 -0
- package/src/enums/xrpl-definitions.ts +32 -0
- package/src/hash-prefixes.ts +40 -0
- package/src/hashes.ts +76 -0
- package/src/index.ts +141 -0
- package/src/ledger-hashes.ts +187 -0
- package/src/quality.ts +39 -0
- package/src/serdes/binary-parser.ts +217 -0
- package/src/serdes/binary-serializer.ts +166 -0
- package/src/shamap.ts +186 -0
- package/src/types/account-id.ts +86 -0
- package/src/types/amount.ts +256 -0
- package/src/types/blob.ts +43 -0
- package/src/types/currency.ts +140 -0
- package/src/types/hash-128.ts +33 -0
- package/src/types/hash-160.ts +20 -0
- package/src/types/hash-256.ts +16 -0
- package/src/types/hash.ts +81 -0
- package/src/types/index.ts +61 -0
- package/src/types/issue.ts +96 -0
- package/src/types/path-set.ts +290 -0
- package/src/types/serialized-type.ts +120 -0
- package/src/types/st-array.ts +107 -0
- package/src/types/st-object.ts +192 -0
- package/src/types/uint-16.ts +49 -0
- package/src/types/uint-32.ts +56 -0
- package/src/types/uint-64.ts +105 -0
- package/src/types/uint-8.ts +49 -0
- package/src/types/uint.ts +57 -0
- package/src/types/vector-256.ts +84 -0
- package/test/amount.test.js +0 -43
- package/test/binary-json.test.js +0 -45
- package/test/binary-parser.test.js +0 -396
- package/test/binary-serializer.test.js +0 -289
- package/test/definitions.test.js +0 -160
- package/test/fixtures/account-tx-transactions.db +0 -0
- package/test/fixtures/codec-fixtures.json +0 -4466
- package/test/fixtures/data-driven-tests.json +0 -2919
- package/test/fixtures/delivermin-tx-binary.json +0 -1
- package/test/fixtures/delivermin-tx.json +0 -98
- package/test/fixtures/deposit-preauth-tx-binary.json +0 -1
- package/test/fixtures/deposit-preauth-tx-meta-binary.json +0 -1
- package/test/fixtures/deposit-preauth-tx.json +0 -58
- package/test/fixtures/escrow-cancel-binary.json +0 -1
- package/test/fixtures/escrow-cancel-tx.json +0 -6
- package/test/fixtures/escrow-create-binary.json +0 -1
- package/test/fixtures/escrow-create-tx.json +0 -10
- package/test/fixtures/escrow-finish-binary.json +0 -1
- package/test/fixtures/escrow-finish-meta-binary.json +0 -1
- package/test/fixtures/escrow-finish-tx.json +0 -95
- package/test/fixtures/ledger-full-38129.json +0 -1
- package/test/fixtures/ledger-full-40000.json +0 -1
- package/test/fixtures/negative-unl.json +0 -12
- package/test/fixtures/nf-token.json +0 -547
- package/test/fixtures/payment-channel-claim-binary.json +0 -1
- package/test/fixtures/payment-channel-claim-tx.json +0 -8
- package/test/fixtures/payment-channel-create-binary.json +0 -1
- package/test/fixtures/payment-channel-create-tx.json +0 -11
- package/test/fixtures/payment-channel-fund-binary.json +0 -1
- package/test/fixtures/payment-channel-fund-tx.json +0 -7
- package/test/fixtures/signerlistset-tx-binary.json +0 -1
- package/test/fixtures/signerlistset-tx-meta-binary.json +0 -1
- package/test/fixtures/signerlistset-tx.json +0 -94
- package/test/fixtures/ticket-create-binary.json +0 -1
- package/test/fixtures/ticket-create-tx.json +0 -7
- package/test/fixtures/x-codec-fixtures.json +0 -188
- package/test/hash.test.js +0 -135
- package/test/ledger.test.js +0 -29
- package/test/lower-case-hex.test.js +0 -46
- package/test/pseudo-transaction.test.js +0 -38
- package/test/quality.test.js +0 -15
- package/test/shamap.test.js +0 -89
- package/test/signing-data-encoding.test.js +0 -242
- package/test/tx-encode-decode.test.js +0 -119
- package/test/types.test.js +0 -34
- package/test/uint.test.js +0 -148
- package/test/utils.js +0 -30
- package/test/x-address.test.js +0 -181
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as assert from 'assert'
|
|
2
|
+
import { ShaMap, ShaMapNode, ShaMapLeaf } from './shamap'
|
|
3
|
+
import { HashPrefix } from './hash-prefixes'
|
|
4
|
+
import { Sha512Half } from './hashes'
|
|
5
|
+
import { BinarySerializer, serializeObject } from './binary'
|
|
6
|
+
import { Hash256 } from './types/hash-256'
|
|
7
|
+
import { STObject } from './types/st-object'
|
|
8
|
+
import { UInt64 } from './types/uint-64'
|
|
9
|
+
import { UInt32 } from './types/uint-32'
|
|
10
|
+
import { UInt8 } from './types/uint-8'
|
|
11
|
+
import { BinaryParser } from './serdes/binary-parser'
|
|
12
|
+
import { JsonObject } from './types/serialized-type'
|
|
13
|
+
import bigInt = require('big-integer')
|
|
14
|
+
import { XrplDefinitionsBase } from './enums'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Computes the hash of a list of objects
|
|
18
|
+
*
|
|
19
|
+
* @param itemizer Converts an item into a format that can be added to SHAMap
|
|
20
|
+
* @param itemsJson Array of items to add to a SHAMap
|
|
21
|
+
* @returns the hash of the SHAMap
|
|
22
|
+
*/
|
|
23
|
+
function computeHash(
|
|
24
|
+
itemizer: (item: JsonObject) => [Hash256?, ShaMapNode?, ShaMapLeaf?],
|
|
25
|
+
itemsJson: Array<JsonObject>,
|
|
26
|
+
): Hash256 {
|
|
27
|
+
const map = new ShaMap()
|
|
28
|
+
itemsJson.forEach((item) => map.addItem(...itemizer(item)))
|
|
29
|
+
return map.hash()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Interface describing a transaction item
|
|
34
|
+
*/
|
|
35
|
+
interface transactionItemObject extends JsonObject {
|
|
36
|
+
hash: string
|
|
37
|
+
metaData: JsonObject
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert a transaction into an index and an item
|
|
42
|
+
*
|
|
43
|
+
* @param json transaction with metadata
|
|
44
|
+
* @returns a tuple of index and item to be added to SHAMap
|
|
45
|
+
*/
|
|
46
|
+
function transactionItemizer(
|
|
47
|
+
json: transactionItemObject,
|
|
48
|
+
): [Hash256, ShaMapNode, undefined] {
|
|
49
|
+
assert.ok(json.hash)
|
|
50
|
+
const index = Hash256.from(json.hash)
|
|
51
|
+
const item = {
|
|
52
|
+
hashPrefix() {
|
|
53
|
+
return HashPrefix.transaction
|
|
54
|
+
},
|
|
55
|
+
toBytesSink(sink) {
|
|
56
|
+
const serializer = new BinarySerializer(sink)
|
|
57
|
+
serializer.writeLengthEncoded(STObject.from(json))
|
|
58
|
+
serializer.writeLengthEncoded(STObject.from(json.metaData))
|
|
59
|
+
},
|
|
60
|
+
} as ShaMapNode
|
|
61
|
+
return [index, item, undefined]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Interface describing an entry item
|
|
66
|
+
*/
|
|
67
|
+
interface entryItemObject extends JsonObject {
|
|
68
|
+
index: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Convert an entry to a pair Hash256 and ShaMapNode
|
|
73
|
+
*
|
|
74
|
+
* @param json JSON describing a ledger entry item
|
|
75
|
+
* @returns a tuple of index and item to be added to SHAMap
|
|
76
|
+
*/
|
|
77
|
+
function entryItemizer(
|
|
78
|
+
json: entryItemObject,
|
|
79
|
+
): [Hash256, ShaMapNode, undefined] {
|
|
80
|
+
const index = Hash256.from(json.index)
|
|
81
|
+
const bytes = serializeObject(json)
|
|
82
|
+
const item = {
|
|
83
|
+
hashPrefix() {
|
|
84
|
+
return HashPrefix.accountStateEntry
|
|
85
|
+
},
|
|
86
|
+
toBytesSink(sink) {
|
|
87
|
+
sink.put(bytes)
|
|
88
|
+
},
|
|
89
|
+
} as ShaMapNode
|
|
90
|
+
return [index, item, undefined]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Function computing the hash of a transaction tree
|
|
95
|
+
*
|
|
96
|
+
* @param param An array of transaction objects to hash
|
|
97
|
+
* @returns A Hash256 object
|
|
98
|
+
*/
|
|
99
|
+
function transactionTreeHash(param: Array<JsonObject>): Hash256 {
|
|
100
|
+
const itemizer = transactionItemizer as (
|
|
101
|
+
json: JsonObject,
|
|
102
|
+
) => [Hash256, ShaMapNode, undefined]
|
|
103
|
+
return computeHash(itemizer, param)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Function computing the hash of accountState
|
|
108
|
+
*
|
|
109
|
+
* @param param A list of accountStates hash
|
|
110
|
+
* @returns A Hash256 object
|
|
111
|
+
*/
|
|
112
|
+
function accountStateHash(param: Array<JsonObject>): Hash256 {
|
|
113
|
+
const itemizer = entryItemizer as (
|
|
114
|
+
json: JsonObject,
|
|
115
|
+
) => [Hash256, ShaMapNode, undefined]
|
|
116
|
+
return computeHash(itemizer, param)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Interface describing a ledger header
|
|
121
|
+
*/
|
|
122
|
+
interface ledgerObject {
|
|
123
|
+
ledger_index: number
|
|
124
|
+
total_coins: string | number | bigInt.BigInteger
|
|
125
|
+
parent_hash: string
|
|
126
|
+
transaction_hash: string
|
|
127
|
+
account_hash: string
|
|
128
|
+
parent_close_time: number
|
|
129
|
+
close_time: number
|
|
130
|
+
close_time_resolution: number
|
|
131
|
+
close_flags: number
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Serialize and hash a ledger header
|
|
136
|
+
*
|
|
137
|
+
* @param header a ledger header
|
|
138
|
+
* @returns the hash of header
|
|
139
|
+
*/
|
|
140
|
+
function ledgerHash(header: ledgerObject): Hash256 {
|
|
141
|
+
const hash = new Sha512Half()
|
|
142
|
+
hash.put(HashPrefix.ledgerHeader)
|
|
143
|
+
assert.ok(header.parent_close_time !== undefined)
|
|
144
|
+
assert.ok(header.close_flags !== undefined)
|
|
145
|
+
|
|
146
|
+
UInt32.from<number>(header.ledger_index).toBytesSink(hash)
|
|
147
|
+
UInt64.from<bigInt.BigInteger>(
|
|
148
|
+
bigInt(String(header.total_coins)),
|
|
149
|
+
).toBytesSink(hash)
|
|
150
|
+
Hash256.from<string>(header.parent_hash).toBytesSink(hash)
|
|
151
|
+
Hash256.from<string>(header.transaction_hash).toBytesSink(hash)
|
|
152
|
+
Hash256.from<string>(header.account_hash).toBytesSink(hash)
|
|
153
|
+
UInt32.from<number>(header.parent_close_time).toBytesSink(hash)
|
|
154
|
+
UInt32.from<number>(header.close_time).toBytesSink(hash)
|
|
155
|
+
UInt8.from<number>(header.close_time_resolution).toBytesSink(hash)
|
|
156
|
+
UInt8.from<number>(header.close_flags).toBytesSink(hash)
|
|
157
|
+
return hash.finish()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Decodes a serialized ledger header
|
|
162
|
+
*
|
|
163
|
+
* @param binary A serialized ledger header
|
|
164
|
+
* @param definitions Type definitions to parse the ledger objects.
|
|
165
|
+
* Used if there are non-default ledger objects to decode.
|
|
166
|
+
* @returns A JSON object describing a ledger header
|
|
167
|
+
*/
|
|
168
|
+
function decodeLedgerData(
|
|
169
|
+
binary: string,
|
|
170
|
+
definitions?: XrplDefinitionsBase,
|
|
171
|
+
): object {
|
|
172
|
+
assert.ok(typeof binary === 'string', 'binary must be a hex string')
|
|
173
|
+
const parser = new BinaryParser(binary, definitions)
|
|
174
|
+
return {
|
|
175
|
+
ledger_index: parser.readUInt32(),
|
|
176
|
+
total_coins: parser.readType(UInt64).valueOf().toString(),
|
|
177
|
+
parent_hash: parser.readType(Hash256).toHex(),
|
|
178
|
+
transaction_hash: parser.readType(Hash256).toHex(),
|
|
179
|
+
account_hash: parser.readType(Hash256).toHex(),
|
|
180
|
+
parent_close_time: parser.readUInt32(),
|
|
181
|
+
close_time: parser.readUInt32(),
|
|
182
|
+
close_time_resolution: parser.readUInt8(),
|
|
183
|
+
close_flags: parser.readUInt8(),
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export { accountStateHash, transactionTreeHash, ledgerHash, decodeLedgerData }
|
package/src/quality.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { coreTypes } from './types'
|
|
2
|
+
import { Decimal } from 'decimal.js'
|
|
3
|
+
import bigInt = require('big-integer')
|
|
4
|
+
import { Buffer } from 'buffer/'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* class for encoding and decoding quality
|
|
8
|
+
*/
|
|
9
|
+
class quality {
|
|
10
|
+
/**
|
|
11
|
+
* Encode quality amount
|
|
12
|
+
*
|
|
13
|
+
* @param arg string representation of an amount
|
|
14
|
+
* @returns Serialized quality
|
|
15
|
+
*/
|
|
16
|
+
static encode(quality: string): Buffer {
|
|
17
|
+
const decimal = new Decimal(quality)
|
|
18
|
+
const exponent = decimal.e - 15
|
|
19
|
+
const qualityString = decimal.times(`1e${-exponent}`).abs().toString()
|
|
20
|
+
const bytes = coreTypes.UInt64.from(bigInt(qualityString)).toBytes()
|
|
21
|
+
bytes[0] = exponent + 100
|
|
22
|
+
return bytes
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Decode quality amount
|
|
27
|
+
*
|
|
28
|
+
* @param arg hex-string denoting serialized quality
|
|
29
|
+
* @returns deserialized quality
|
|
30
|
+
*/
|
|
31
|
+
static decode(quality: string): Decimal {
|
|
32
|
+
const bytes = Buffer.from(quality, 'hex').slice(-8)
|
|
33
|
+
const exponent = bytes[0] - 100
|
|
34
|
+
const mantissa = new Decimal(`0x${bytes.slice(1).toString('hex')}`)
|
|
35
|
+
return mantissa.times(`1e${exponent}`)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { quality }
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import * as assert from 'assert'
|
|
2
|
+
import {
|
|
3
|
+
XrplDefinitionsBase,
|
|
4
|
+
DEFAULT_DEFINITIONS,
|
|
5
|
+
FieldInstance,
|
|
6
|
+
} from '../enums'
|
|
7
|
+
import { type SerializedType } from '../types/serialized-type'
|
|
8
|
+
import { Buffer } from 'buffer/'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* BinaryParser is used to compute fields and values from a HexString
|
|
12
|
+
*/
|
|
13
|
+
class BinaryParser {
|
|
14
|
+
private bytes: Buffer
|
|
15
|
+
definitions: XrplDefinitionsBase
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize bytes to a hex string
|
|
19
|
+
*
|
|
20
|
+
* @param hexBytes a hex string
|
|
21
|
+
* @param definitions Rippled definitions used to parse the values of transaction types and such.
|
|
22
|
+
* Can be customized for sidechains and amendments.
|
|
23
|
+
*/
|
|
24
|
+
constructor(
|
|
25
|
+
hexBytes: string,
|
|
26
|
+
definitions: XrplDefinitionsBase = DEFAULT_DEFINITIONS,
|
|
27
|
+
) {
|
|
28
|
+
this.bytes = Buffer.from(hexBytes, 'hex')
|
|
29
|
+
this.definitions = definitions
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Peek the first byte of the BinaryParser
|
|
34
|
+
*
|
|
35
|
+
* @returns The first byte of the BinaryParser
|
|
36
|
+
*/
|
|
37
|
+
peek(): number {
|
|
38
|
+
assert.ok(this.bytes.byteLength !== 0)
|
|
39
|
+
return this.bytes[0]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Consume the first n bytes of the BinaryParser
|
|
44
|
+
*
|
|
45
|
+
* @param n the number of bytes to skip
|
|
46
|
+
*/
|
|
47
|
+
skip(n: number): void {
|
|
48
|
+
assert.ok(n <= this.bytes.byteLength)
|
|
49
|
+
this.bytes = this.bytes.slice(n)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* read the first n bytes from the BinaryParser
|
|
54
|
+
*
|
|
55
|
+
* @param n The number of bytes to read
|
|
56
|
+
* @return The bytes
|
|
57
|
+
*/
|
|
58
|
+
read(n: number): Buffer {
|
|
59
|
+
assert.ok(n <= this.bytes.byteLength)
|
|
60
|
+
|
|
61
|
+
const slice = this.bytes.slice(0, n)
|
|
62
|
+
this.skip(n)
|
|
63
|
+
return slice
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Read an integer of given size
|
|
68
|
+
*
|
|
69
|
+
* @param n The number of bytes to read
|
|
70
|
+
* @return The number represented by those bytes
|
|
71
|
+
*/
|
|
72
|
+
readUIntN(n: number): number {
|
|
73
|
+
assert.ok(0 < n && n <= 4, 'invalid n')
|
|
74
|
+
return this.read(n).reduce((a, b) => (a << 8) | b) >>> 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
readUInt8(): number {
|
|
78
|
+
return this.readUIntN(1)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
readUInt16(): number {
|
|
82
|
+
return this.readUIntN(2)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
readUInt32(): number {
|
|
86
|
+
return this.readUIntN(4)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
size(): number {
|
|
90
|
+
return this.bytes.byteLength
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
end(customEnd?: number): boolean {
|
|
94
|
+
const length = this.bytes.byteLength
|
|
95
|
+
return length === 0 || (customEnd !== undefined && length <= customEnd)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Reads variable length encoded bytes
|
|
100
|
+
*
|
|
101
|
+
* @return The variable length bytes
|
|
102
|
+
*/
|
|
103
|
+
readVariableLength(): Buffer {
|
|
104
|
+
return this.read(this.readVariableLengthLength())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Reads the length of the variable length encoded bytes
|
|
109
|
+
*
|
|
110
|
+
* @return The length of the variable length encoded bytes
|
|
111
|
+
*/
|
|
112
|
+
readVariableLengthLength(): number {
|
|
113
|
+
const b1 = this.readUInt8()
|
|
114
|
+
if (b1 <= 192) {
|
|
115
|
+
return b1
|
|
116
|
+
} else if (b1 <= 240) {
|
|
117
|
+
const b2 = this.readUInt8()
|
|
118
|
+
return 193 + (b1 - 193) * 256 + b2
|
|
119
|
+
} else if (b1 <= 254) {
|
|
120
|
+
const b2 = this.readUInt8()
|
|
121
|
+
const b3 = this.readUInt8()
|
|
122
|
+
return 12481 + (b1 - 241) * 65536 + b2 * 256 + b3
|
|
123
|
+
}
|
|
124
|
+
throw new Error('Invalid variable length indicator')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Reads the field ordinal from the BinaryParser
|
|
129
|
+
*
|
|
130
|
+
* @return Field ordinal
|
|
131
|
+
*/
|
|
132
|
+
readFieldOrdinal(): number {
|
|
133
|
+
let type = this.readUInt8()
|
|
134
|
+
let nth = type & 15
|
|
135
|
+
type >>= 4
|
|
136
|
+
|
|
137
|
+
if (type === 0) {
|
|
138
|
+
type = this.readUInt8()
|
|
139
|
+
if (type === 0 || type < 16) {
|
|
140
|
+
throw new Error('Cannot read FieldOrdinal, type_code out of range')
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (nth === 0) {
|
|
145
|
+
nth = this.readUInt8()
|
|
146
|
+
if (nth === 0 || nth < 16) {
|
|
147
|
+
throw new Error('Cannot read FieldOrdinal, field_code out of range')
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return (type << 16) | nth
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Read the field from the BinaryParser
|
|
156
|
+
*
|
|
157
|
+
* @return The field represented by the bytes at the head of the BinaryParser
|
|
158
|
+
*/
|
|
159
|
+
readField(): FieldInstance {
|
|
160
|
+
return this.definitions.field.fromString(this.readFieldOrdinal().toString())
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Read a given type from the BinaryParser
|
|
165
|
+
*
|
|
166
|
+
* @param type The type that you want to read from the BinaryParser
|
|
167
|
+
* @return The instance of that type read from the BinaryParser
|
|
168
|
+
*/
|
|
169
|
+
readType(type: typeof SerializedType): SerializedType {
|
|
170
|
+
return type.fromParser(this)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the type associated with a given field
|
|
175
|
+
*
|
|
176
|
+
* @param field The field that you wan to get the type of
|
|
177
|
+
* @return The type associated with the given field
|
|
178
|
+
*/
|
|
179
|
+
typeForField(field: FieldInstance): typeof SerializedType {
|
|
180
|
+
return field.associatedType
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Read value of the type specified by field from the BinaryParser
|
|
185
|
+
*
|
|
186
|
+
* @param field The field that you want to get the associated value for
|
|
187
|
+
* @return The value associated with the given field
|
|
188
|
+
*/
|
|
189
|
+
readFieldValue(field: FieldInstance): SerializedType {
|
|
190
|
+
const type = this.typeForField(field)
|
|
191
|
+
if (!type) {
|
|
192
|
+
throw new Error(`unsupported: (${field.name}, ${field.type.name})`)
|
|
193
|
+
}
|
|
194
|
+
const sizeHint = field.isVariableLengthEncoded
|
|
195
|
+
? this.readVariableLengthLength()
|
|
196
|
+
: undefined
|
|
197
|
+
const value = type.fromParser(this, sizeHint)
|
|
198
|
+
if (value === undefined) {
|
|
199
|
+
throw new Error(
|
|
200
|
+
`fromParser for (${field.name}, ${field.type.name}) -> undefined `,
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
return value
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get the next field and value from the BinaryParser
|
|
208
|
+
*
|
|
209
|
+
* @return The field and value
|
|
210
|
+
*/
|
|
211
|
+
readFieldAndValue(): [FieldInstance, SerializedType] {
|
|
212
|
+
const field = this.readField()
|
|
213
|
+
return [field, this.readFieldValue(field)]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export { BinaryParser }
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as assert from 'assert'
|
|
2
|
+
import { FieldInstance } from '../enums'
|
|
3
|
+
import { type SerializedType } from '../types/serialized-type'
|
|
4
|
+
import { Buffer } from 'buffer/'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Bytes list is a collection of buffer objects
|
|
8
|
+
*/
|
|
9
|
+
class BytesList {
|
|
10
|
+
private bytesArray: Array<Buffer> = []
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the total number of bytes in the BytesList
|
|
14
|
+
*
|
|
15
|
+
* @return the number of bytes
|
|
16
|
+
*/
|
|
17
|
+
public getLength(): number {
|
|
18
|
+
return Buffer.concat(this.bytesArray).byteLength
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Put bytes in the BytesList
|
|
23
|
+
*
|
|
24
|
+
* @param bytesArg A Buffer
|
|
25
|
+
* @return this BytesList
|
|
26
|
+
*/
|
|
27
|
+
public put(bytesArg: Buffer): BytesList {
|
|
28
|
+
const bytes = Buffer.from(bytesArg) // Temporary, to catch instances of Uint8Array being passed in
|
|
29
|
+
this.bytesArray.push(bytes)
|
|
30
|
+
return this
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Write this BytesList to the back of another bytes list
|
|
35
|
+
*
|
|
36
|
+
* @param list The BytesList to write to
|
|
37
|
+
*/
|
|
38
|
+
public toBytesSink(list: BytesList): void {
|
|
39
|
+
list.put(this.toBytes())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public toBytes(): Buffer {
|
|
43
|
+
return Buffer.concat(this.bytesArray)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toHex(): string {
|
|
47
|
+
return this.toBytes().toString('hex').toUpperCase()
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* BinarySerializer is used to write fields and values to buffers
|
|
53
|
+
*/
|
|
54
|
+
class BinarySerializer {
|
|
55
|
+
private sink: BytesList = new BytesList()
|
|
56
|
+
|
|
57
|
+
constructor(sink: BytesList) {
|
|
58
|
+
this.sink = sink
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Write a value to this BinarySerializer
|
|
63
|
+
*
|
|
64
|
+
* @param value a SerializedType value
|
|
65
|
+
*/
|
|
66
|
+
write(value: SerializedType): void {
|
|
67
|
+
value.toBytesSink(this.sink)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Write bytes to this BinarySerializer
|
|
72
|
+
*
|
|
73
|
+
* @param bytes the bytes to write
|
|
74
|
+
*/
|
|
75
|
+
put(bytes: Buffer): void {
|
|
76
|
+
this.sink.put(bytes)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Write a value of a given type to this BinarySerializer
|
|
81
|
+
*
|
|
82
|
+
* @param type the type to write
|
|
83
|
+
* @param value a value of that type
|
|
84
|
+
*/
|
|
85
|
+
writeType(type: typeof SerializedType, value: SerializedType): void {
|
|
86
|
+
this.write(type.from(value))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Write BytesList to this BinarySerializer
|
|
91
|
+
*
|
|
92
|
+
* @param bl BytesList to write to BinarySerializer
|
|
93
|
+
*/
|
|
94
|
+
writeBytesList(bl: BytesList): void {
|
|
95
|
+
bl.toBytesSink(this.sink)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Calculate the header of Variable Length encoded bytes
|
|
100
|
+
*
|
|
101
|
+
* @param length the length of the bytes
|
|
102
|
+
*/
|
|
103
|
+
private encodeVariableLength(length: number): Buffer {
|
|
104
|
+
const lenBytes = Buffer.alloc(3)
|
|
105
|
+
if (length <= 192) {
|
|
106
|
+
lenBytes[0] = length
|
|
107
|
+
return lenBytes.slice(0, 1)
|
|
108
|
+
} else if (length <= 12480) {
|
|
109
|
+
length -= 193
|
|
110
|
+
lenBytes[0] = 193 + (length >>> 8)
|
|
111
|
+
lenBytes[1] = length & 0xff
|
|
112
|
+
return lenBytes.slice(0, 2)
|
|
113
|
+
} else if (length <= 918744) {
|
|
114
|
+
length -= 12481
|
|
115
|
+
lenBytes[0] = 241 + (length >>> 16)
|
|
116
|
+
lenBytes[1] = (length >> 8) & 0xff
|
|
117
|
+
lenBytes[2] = length & 0xff
|
|
118
|
+
return lenBytes.slice(0, 3)
|
|
119
|
+
}
|
|
120
|
+
throw new Error('Overflow error')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Write field and value to BinarySerializer
|
|
125
|
+
*
|
|
126
|
+
* @param field field to write to BinarySerializer
|
|
127
|
+
* @param value value to write to BinarySerializer
|
|
128
|
+
*/
|
|
129
|
+
writeFieldAndValue(
|
|
130
|
+
field: FieldInstance,
|
|
131
|
+
value: SerializedType,
|
|
132
|
+
isUnlModifyWorkaround = false,
|
|
133
|
+
): void {
|
|
134
|
+
const associatedValue = field.associatedType.from(value)
|
|
135
|
+
assert.ok(associatedValue.toBytesSink !== undefined)
|
|
136
|
+
assert.ok(field.name !== undefined)
|
|
137
|
+
|
|
138
|
+
this.sink.put(field.header)
|
|
139
|
+
|
|
140
|
+
if (field.isVariableLengthEncoded) {
|
|
141
|
+
this.writeLengthEncoded(associatedValue, isUnlModifyWorkaround)
|
|
142
|
+
} else {
|
|
143
|
+
associatedValue.toBytesSink(this.sink)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Write a variable length encoded value to the BinarySerializer
|
|
149
|
+
*
|
|
150
|
+
* @param value length encoded value to write to BytesList
|
|
151
|
+
*/
|
|
152
|
+
public writeLengthEncoded(
|
|
153
|
+
value: SerializedType,
|
|
154
|
+
isUnlModifyWorkaround = false,
|
|
155
|
+
): void {
|
|
156
|
+
const bytes = new BytesList()
|
|
157
|
+
if (!isUnlModifyWorkaround) {
|
|
158
|
+
// this part doesn't happen for the Account field in a UNLModify transaction
|
|
159
|
+
value.toBytesSink(bytes)
|
|
160
|
+
}
|
|
161
|
+
this.put(this.encodeVariableLength(bytes.getLength()))
|
|
162
|
+
this.writeBytesList(bytes)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export { BytesList, BinarySerializer }
|