Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #2361 - Custom definitions in STArray»STObject #2362

Merged
merged 6 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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