Support encoding and decoding of NegativeUNL pseudo-transactions (#89)

* Support encoding and decoding of NegativeUNL pseudo-transactions
This commit is contained in:
Nathan Nichols
2020-08-13 12:20:12 -05:00
parent 2e6c68ba73
commit 30ff63e653
8 changed files with 161 additions and 17 deletions

View File

@@ -40,7 +40,8 @@
"Check": 67, "Check": 67,
"Nickname": 110, "Nickname": 110,
"Contract": 99, "Contract": 99,
"GeneratorMap": 103 "GeneratorMap": 103,
"NegativeUNL": 78
}, },
"FIELDS": [ "FIELDS": [
[ [
@@ -1023,6 +1024,36 @@
"type": "Blob" "type": "Blob"
} }
], ],
[
"UNLModifyValidator",
{
"nth": 19,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Blob"
}
],
[
"ValidatorToDisable",
{
"nth": 20,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Blob"
}
],
[
"ValidatorToReEnable",
{
"nth": 20,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Blob"
}
],
[ [
"Account", "Account",
{ {
@@ -1233,6 +1264,16 @@
"type": "STObject" "type": "STObject"
} }
], ],
[
"DisabledValidator",
{
"nth": 19,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "STObject"
}
],
[ [
"ArrayEndMarker", "ArrayEndMarker",
{ {
@@ -1323,6 +1364,16 @@
"type": "STArray" "type": "STArray"
} }
], ],
[
"DisabledValidators",
{
"nth": 17,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "STArray"
}
],
[ [
"CloseResolution", "CloseResolution",
{ {
@@ -1483,6 +1534,16 @@
"type": "UInt32" "type": "UInt32"
} }
], ],
[
"BeginLedgerSeq",
{
"nth": 40,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[ [
"Channel", "Channel",
{ {
@@ -1523,6 +1584,16 @@
"type": "UInt8" "type": "UInt8"
} }
], ],
[
"UNLModifyDisabling",
{
"nth": 17,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt8"
}
],
[ [
"DestinationNode", "DestinationNode",
{ {
@@ -1685,6 +1756,7 @@
"AccountDelete": 21, "AccountDelete": 21,
"EnableAmendment": 100, "EnableAmendment": 100,
"SetFee": 101 "SetFee": 101,
"UNLModify": 102
} }
} }

View File

@@ -7,7 +7,7 @@ import { Hash160 } from "./hash-160";
class AccountID extends Hash160 { class AccountID extends Hash160 {
static readonly defaultAccountID: AccountID = new AccountID(Buffer.alloc(20)); static readonly defaultAccountID: AccountID = new AccountID(Buffer.alloc(20));
constructor(bytes: Buffer) { constructor(bytes?: Buffer) {
super(bytes ?? AccountID.defaultAccountID.bytes); super(bytes ?? AccountID.defaultAccountID.bytes);
} }
@@ -23,6 +23,10 @@ class AccountID extends Hash160 {
} }
if (typeof value === "string") { if (typeof value === "string") {
if (value === "") {
return new AccountID();
}
return /^r/.test(value) return /^r/.test(value)
? this.fromBase58(value) ? this.fromBase58(value)
: new AccountID(Buffer.from(value, "hex")); : new AccountID(Buffer.from(value, "hex"));

View File

@@ -7,7 +7,11 @@ class Hash160 extends Hash {
static readonly width = 20; static readonly width = 20;
static readonly ZERO_160: Hash160 = new Hash160(Buffer.alloc(Hash160.width)); static readonly ZERO_160: Hash160 = new Hash160(Buffer.alloc(Hash160.width));
constructor(bytes: Buffer) { constructor(bytes?: Buffer) {
if (bytes && bytes.byteLength === 0) {
bytes = Hash160.ZERO_160.bytes;
}
super(bytes ?? Hash160.ZERO_160.bytes); super(bytes ?? Hash160.ZERO_160.bytes);
} }
} }

View File

@@ -9,24 +9,26 @@ class Hash extends Comparable {
constructor(bytes: Buffer) { constructor(bytes: Buffer) {
super(bytes); super(bytes);
if (this.bytes.byteLength !== (this.constructor as typeof Hash).width) {
throw new Error(`Invalid Hash length ${this.bytes.byteLength}`);
}
} }
/** /**
* Construct a Hash object from an existing Hash object or a hex-string * Construct a Hash object from an existing Hash object or a hex-string
* *
* @param value A hash object or hex-string of a hash * @param value A hash object or hex-string of a hash
*/ */
static from<T extends Hash | string>(value: T): Hash { static from<T extends Hash | string>(value: T): Hash {
if(value instanceof this) { if (value instanceof this) {
return value return value;
} }
if(typeof value === "string") { if (typeof value === "string") {
return new this(Buffer.from(value, "hex")); return new this(Buffer.from(value, "hex"));
} }
throw new Error("Cannot construct Hash from given value"); throw new Error("Cannot construct Hash from given value");
} }
/** /**

View File

@@ -29,9 +29,10 @@ interface HopObject extends JsonObject {
* TypeGuard for HopObject * TypeGuard for HopObject
*/ */
function isHopObject(arg): arg is HopObject { function isHopObject(arg): arg is HopObject {
return (arg.issuer !== undefined || return (
arg.account !== undefined || arg.issuer !== undefined ||
arg.currency !== undefined arg.account !== undefined ||
arg.currency !== undefined
); );
} }
@@ -40,9 +41,9 @@ function isHopObject(arg): arg is HopObject {
*/ */
function isPathSet(arg): arg is Array<Array<HopObject>> { function isPathSet(arg): arg is Array<Array<HopObject>> {
return ( return (
Array.isArray(arg) && arg.length === 0 || (Array.isArray(arg) && arg.length === 0) ||
Array.isArray(arg) && Array.isArray(arg[0]) && arg[0].length === 0 || (Array.isArray(arg) && Array.isArray(arg[0]) && arg[0].length === 0) ||
Array.isArray(arg) && Array.isArray(arg[0]) && isHopObject(arg[0][0]) (Array.isArray(arg) && Array.isArray(arg[0]) && isHopObject(arg[0][0]))
); );
} }

View File

@@ -1,7 +1,7 @@
/* eslint-disable func-style */ /* eslint-disable func-style */
const { binary } = require('../dist/coretypes') const { binary } = require('../dist/coretypes')
const { encode } = require('../dist') const { encode, decode } = require('../dist')
const { makeParser, BytesList, BinarySerializer } = binary const { makeParser, BytesList, BinarySerializer } = binary
const { coreTypes } = require('../dist/types') const { coreTypes } = require('../dist/types')
const { UInt8, UInt16, UInt32, UInt64, STObject } = coreTypes const { UInt8, UInt16, UInt32, UInt64, STObject } = coreTypes
@@ -50,6 +50,8 @@ const PaymentChannel = {
} }
} }
const NegativeUNL = require('./fixtures/negative-unl.json');
function bytesListTest () { function bytesListTest () {
const list = new BytesList().put(Buffer.from([0])).put(Buffer.from([2, 3])).put(Buffer.from([4, 5])) const list = new BytesList().put(Buffer.from([0])).put(Buffer.from([2, 3])).put(Buffer.from([4, 5]))
test('is an Array<Buffer>', function () { test('is an Array<Buffer>', function () {
@@ -171,6 +173,15 @@ function PaymentChannelTest () {
}) })
} }
function NegativeUNLTest () {
test('can serialize NegativeUNL', () => {
expect(encode(NegativeUNL.tx)).toEqual(NegativeUNL.binary);
})
test('can deserialize NegativeUNL', () => {
expect(decode(NegativeUNL.binary)).toEqual(NegativeUNL.tx);
})
}
describe('Binary Serialization', function() { describe('Binary Serialization', function() {
describe('nestedObjectTests', nestedObjectTests); describe('nestedObjectTests', nestedObjectTests);
describe('BytesList', bytesListTest); describe('BytesList', bytesListTest);
@@ -179,4 +190,5 @@ describe('Binary Serialization', function() {
describe('SignerListSet', SignerListSetTest); describe('SignerListSet', SignerListSetTest);
describe('Escrow', EscrowTest); describe('Escrow', EscrowTest);
describe('PaymentChannel', PaymentChannelTest); describe('PaymentChannel', PaymentChannelTest);
describe('NegativeUNLTest', NegativeUNLTest);
}) })

View File

@@ -0,0 +1,12 @@
{
"binary": "120066240000000026000003006840000000000000007300701321ED9D593004CC501CACD261BD8E31E863F2B3F6CA69505E7FD54DA8F5690BEFB7AE8114000000000000000000000000000000000000000000101101",
"tx": {
"UNLModifyDisabling": 1,
"LedgerSequence": 768,
"UNLModifyValidator": "ED9D593004CC501CACD261BD8E31E863F2B3F6CA69505E7FD54DA8F5690BEFB7AE",
"TransactionType": "UNLModify",
"Account": "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
"Sequence": 0,
"Fee": "0",
"SigningPubKey": ""}
}

View File

@@ -0,0 +1,37 @@
const { encode, decode } = require('../dist')
let json = {
"Account": "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
"Sequence": 0,
"Fee": "0",
"SigningPubKey": "",
"Signature": ""
}
let json_blank_acct = {
"Account": "",
"Sequence": 0,
"Fee": "0",
"SigningPubKey": "",
"Signature": ""
}
let binary = "24000000006840000000000000007300760081140000000000000000000000000000000000000000"
describe("Can encode Pseudo Transactions", () => {
test("Correctly encodes Pseudo Transaciton", () => {
expect(encode(json)).toEqual(binary);
})
test("Can decode account objects", () => {
expect(decode(encode(json))).toEqual(json);
})
test("Blank AccountID is ACCOUNT_ZERO", () => {
expect(encode(json_blank_acct)).toEqual(binary)
})
test("Decodes Blank AccountID", () => {
expect(decode(encode(json_blank_acct))).toEqual(json);
})
})