Skip to content

Commit

Permalink
Fix #2361 - Custom definitions in STArray»STObject (#2362)
Browse files Browse the repository at this point in the history
Custom binary codec fixes xrpl.js#2361 - missed param def
  • Loading branch information
WietseWind authored Jul 14, 2023
1 parent c523a7c commit d47a277
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 10 deletions.
6 changes: 5 additions & 1 deletion packages/ripple-binary-codec/src/types/serialized-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BytesList } from '../serdes/binary-serializer'
import { BinaryParser } from '../serdes/binary-parser'
import bigInt = require('big-integer')
import { Buffer } from 'buffer/'
import { XrplDefinitionsBase } from '../enums'

type JSON = string | number | boolean | null | undefined | JSON[] | JsonObject

Expand Down Expand Up @@ -64,9 +65,12 @@ class SerializedType {
/**
* Return the JSON representation of a SerializedType
*
* @param _definitions rippled definitions used to parse the values of transaction types and such.
* Unused in default, but used in STObject, STArray
* Can be customized for sidechains and amendments.
* @returns any type, if not overloaded returns hexString representation of bytes
*/
toJSON(): JSON {
toJSON(_definitions?: XrplDefinitionsBase): JSON {
return this.toHex()
}

Expand Down
18 changes: 13 additions & 5 deletions packages/ripple-binary-codec/src/types/st-array.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DEFAULT_DEFINITIONS, XrplDefinitionsBase } from '../enums'
import { SerializedType, JsonObject } from './serialized-type'
import { STObject } from './st-object'
import { BinaryParser } from '../serdes/binary-parser'
Expand Down Expand Up @@ -51,17 +52,21 @@ class STArray extends SerializedType {
* Construct an STArray from an Array of JSON Objects
*
* @param value STArray or Array of Objects to parse into an STArray
* @param definitions optional, types and values to use to encode/decode a transaction
* @returns An STArray object
*/
static from<T extends STArray | Array<JsonObject>>(value: T): STArray {
static from<T extends STArray | Array<JsonObject>>(
value: T,
definitions: XrplDefinitionsBase = DEFAULT_DEFINITIONS,
): STArray {
if (value instanceof STArray) {
return value
}

if (isObjects(value)) {
const bytes: Array<Buffer> = []
value.forEach((obj) => {
bytes.push(STObject.from(obj).toBytes())
bytes.push(STObject.from(obj, undefined, definitions).toBytes())
})

bytes.push(ARRAY_END_MARKER)
Expand All @@ -74,12 +79,15 @@ class STArray extends SerializedType {
/**
* Return the JSON representation of this.bytes
*
* @param definitions optional, types and values to use to encode/decode a transaction
* @returns An Array of JSON objects
*/
toJSON(): Array<JsonObject> {
toJSON(
definitions: XrplDefinitionsBase = DEFAULT_DEFINITIONS,
): Array<JsonObject> {
const result: Array<JsonObject> = []

const arrayParser = new BinaryParser(this.toString())
const arrayParser = new BinaryParser(this.toString(), definitions)

while (!arrayParser.end()) {
const field = arrayParser.readField()
Expand All @@ -88,7 +96,7 @@ class STArray extends SerializedType {
}

const outer = {}
outer[field.name] = STObject.fromParser(arrayParser).toJSON()
outer[field.name] = STObject.fromParser(arrayParser).toJSON(definitions)
result.push(outer)
}

Expand Down
15 changes: 11 additions & 4 deletions packages/ripple-binary-codec/src/types/st-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { xAddressToClassicAddress, isValidXAddress } from 'ripple-address-codec'
import { BinaryParser } from '../serdes/binary-parser'
import { BinarySerializer, BytesList } from '../serdes/binary-serializer'
import { Buffer } from 'buffer/'
import { STArray } from './st-array'

const OBJECT_END_MARKER_BYTE = Buffer.from([0xe1])
const OBJECT_END_MARKER = 'ObjectEndMarker'
Expand Down Expand Up @@ -131,9 +132,12 @@ class STObject extends SerializedType {
}

sorted.forEach((field) => {
const associatedValue = field.associatedType.from(
xAddressDecoded[field.name],
)
const associatedValue =
field.type.name === ST_OBJECT
? this.from(xAddressDecoded[field.name], undefined, definitions)
: field.type.name === 'STArray'
? STArray.from(xAddressDecoded[field.name], definitions)
: field.associatedType.from(xAddressDecoded[field.name])

if (associatedValue == undefined) {
throw new TypeError(
Expand Down Expand Up @@ -175,7 +179,10 @@ class STObject extends SerializedType {
if (field.name === OBJECT_END_MARKER) {
break
}
accumulator[field.name] = objectParser.readFieldValue(field).toJSON()

accumulator[field.name] = objectParser
.readFieldValue(field)
.toJSON(definitions)
}

return accumulator
Expand Down
60 changes: 60 additions & 0 deletions packages/ripple-binary-codec/test/definitions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,66 @@ describe('encode and decode using new types as a parameter', function () {
expect(decoded).toStrictEqual(tx)
})

test('can encode and decode a new Field nested in STObject in STArray in STObject', function () {
const tx = {
...txJson,
NewFieldArray: [
{
NewField: {
NewFieldValue: 10,
},
},
],
}

// Before updating the types, undefined fields will be ignored on encode
expect(decode(encode(tx))).not.toStrictEqual(tx)

// Normally this would be generated directly from rippled with something like `server_definitions`.
// Added here to make it easier to see what is actually changing in the definitions.json file.
const definitions = JSON.parse(JSON.stringify(normalDefinitionsJson))

definitions.FIELDS.push([
'NewFieldArray',
{
nth: 100,
isVLEncoded: false,
isSerialized: true,
isSigningField: true,
type: 'STArray',
},
])

definitions.FIELDS.push([
'NewField',
{
nth: 101,
isVLEncoded: false,
isSerialized: true,
isSigningField: true,
type: 'STObject',
},
])

definitions.FIELDS.push([
'NewFieldValue',
{
nth: 102,
isVLEncoded: false,
isSerialized: true,
isSigningField: true,
type: 'UInt32',
},
])

const newDefs = new XrplDefinitions(definitions)

const encoded = encode(tx, newDefs)
expect(() => decode(encoded)).toThrow()
const decoded = decode(encoded, newDefs)
expect(decoded).toStrictEqual(tx)
})

test('can encode and decode a new Type', function () {
const tx = {
...txJson,
Expand Down

0 comments on commit d47a277

Please sign in to comment.