mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-05 13:25:48 +00:00
feature NetworkID (#2216)
Added network id to the base transaction Added network id to the server_info response Added network id to the binary codec Added network id to client and modify on client.connect() Added network id to autofill when rippled_version is 1.11.0 or later or network is hooks testnet Co-authored-by: Phu Pham <ppham@ripple.com> Co-authored-by: pdp2121 <71317875+pdp2121@users.noreply.github.com> Co-authored-by: Jackson Mills <aim4math@gmail.com> Co-authored-by: Mayukha Vadari <mvadari@gmail.com>
This commit is contained in:
@@ -321,6 +321,16 @@
|
|||||||
"type": "UInt16"
|
"type": "UInt16"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"NetworkID",
|
||||||
|
{
|
||||||
|
"nth": 1,
|
||||||
|
"isVLEncoded": false,
|
||||||
|
"isSerialized": true,
|
||||||
|
"isSigningField": true,
|
||||||
|
"type": "UInt32"
|
||||||
|
}
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"Flags",
|
"Flags",
|
||||||
{
|
{
|
||||||
@@ -2176,6 +2186,9 @@
|
|||||||
"telCAN_NOT_QUEUE_BLOCKED": -389,
|
"telCAN_NOT_QUEUE_BLOCKED": -389,
|
||||||
"telCAN_NOT_QUEUE_FEE": -388,
|
"telCAN_NOT_QUEUE_FEE": -388,
|
||||||
"telCAN_NOT_QUEUE_FULL": -387,
|
"telCAN_NOT_QUEUE_FULL": -387,
|
||||||
|
"telWRONG_NETWORK": -386,
|
||||||
|
"telREQUIRES_NETWORK_ID": -385,
|
||||||
|
"telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384,
|
||||||
|
|
||||||
"temMALFORMED": -299,
|
"temMALFORMED": -299,
|
||||||
"temBAD_AMOUNT": -298,
|
"temBAD_AMOUNT": -298,
|
||||||
|
|||||||
@@ -205,6 +205,18 @@ class Client extends EventEmitter {
|
|||||||
*/
|
*/
|
||||||
public readonly maxFeeXRP: string
|
public readonly maxFeeXRP: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network ID of the server this client is connected to
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public networkID: number | undefined
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rippled Version used by the server this client is connected to
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public buildVersion: string | undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Client with a websocket connection to a rippled server.
|
* Creates a new Client with a websocket connection to a rippled server.
|
||||||
*
|
*
|
||||||
@@ -230,8 +242,8 @@ class Client extends EventEmitter {
|
|||||||
this.emit('error', errorCode, errorMessage, data)
|
this.emit('error', errorCode, errorMessage, data)
|
||||||
})
|
})
|
||||||
|
|
||||||
this.connection.on('connected', () => {
|
this.connection.on('reconnect', () => {
|
||||||
this.emit('connected')
|
this.connection.on('connected', () => this.emit('connected'))
|
||||||
})
|
})
|
||||||
|
|
||||||
this.connection.on('disconnected', (code: number) => {
|
this.connection.on('disconnected', (code: number) => {
|
||||||
@@ -568,6 +580,22 @@ class Client extends EventEmitter {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get networkID and buildVersion from server_info
|
||||||
|
*/
|
||||||
|
public async getServerInfo(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const response = await this.request({
|
||||||
|
command: 'server_info',
|
||||||
|
})
|
||||||
|
this.networkID = response.result.info.network_id ?? undefined
|
||||||
|
this.buildVersion = response.result.info.build_version
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console -- Print the error to console but allows client to be connected.
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the Client instance to connect to its rippled server.
|
* Tells the Client instance to connect to its rippled server.
|
||||||
*
|
*
|
||||||
@@ -588,7 +616,10 @@ class Client extends EventEmitter {
|
|||||||
* @category Network
|
* @category Network
|
||||||
*/
|
*/
|
||||||
public async connect(): Promise<void> {
|
public async connect(): Promise<void> {
|
||||||
return this.connection.connect()
|
return this.connection.connect().then(async () => {
|
||||||
|
await this.getServerInfo()
|
||||||
|
this.emit('connected')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -136,6 +136,10 @@ export interface ServerInfoResponse extends BaseResponse {
|
|||||||
* overall network's load factor.
|
* overall network's load factor.
|
||||||
*/
|
*/
|
||||||
load_factor?: number
|
load_factor?: number
|
||||||
|
/**
|
||||||
|
* The network id of the server.
|
||||||
|
*/
|
||||||
|
network_id?: number
|
||||||
/**
|
/**
|
||||||
* Current multiplier to the transaction cost based on
|
* Current multiplier to the transaction cost based on
|
||||||
* load to this server.
|
* load to this server.
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ export interface BaseTransaction {
|
|||||||
* account it says it is from.
|
* account it says it is from.
|
||||||
*/
|
*/
|
||||||
TxnSignature?: string
|
TxnSignature?: string
|
||||||
|
/**
|
||||||
|
* The network id of the transaction.
|
||||||
|
*/
|
||||||
|
NetworkID?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -250,6 +254,9 @@ export function validateBaseTransaction(common: Record<string, unknown>): void {
|
|||||||
) {
|
) {
|
||||||
throw new ValidationError('BaseTransaction: invalid TxnSignature')
|
throw new ValidationError('BaseTransaction: invalid TxnSignature')
|
||||||
}
|
}
|
||||||
|
if (common.NetworkID !== undefined && typeof common.NetworkID !== 'number') {
|
||||||
|
throw new ValidationError('BaseTransaction: invalid NetworkID')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ import getFeeXrp from './getFeeXrp'
|
|||||||
|
|
||||||
// Expire unconfirmed transactions after 20 ledger versions, approximately 1 minute, by default
|
// Expire unconfirmed transactions after 20 ledger versions, approximately 1 minute, by default
|
||||||
const LEDGER_OFFSET = 20
|
const LEDGER_OFFSET = 20
|
||||||
|
// Sidechains are expected to have network IDs above this.
|
||||||
|
// Networks with ID above this restricted number are expected specify an accurate NetworkID field
|
||||||
|
// in every transaction to that chain to prevent replay attacks.
|
||||||
|
// Mainnet and testnet are exceptions. More context: https://github.com/XRPLF/rippled/pull/4370
|
||||||
|
const RESTRICTED_NETWORKS = 1024
|
||||||
|
const REQUIRED_NETWORKID_VERSION = '1.11.0'
|
||||||
|
const HOOKS_TESTNET_ID = 21338
|
||||||
interface ClassicAccountAndTag {
|
interface ClassicAccountAndTag {
|
||||||
classicAccount: string
|
classicAccount: string
|
||||||
tag: number | false | undefined
|
tag: number | false | undefined
|
||||||
@@ -70,8 +77,10 @@ async function autofill<T extends Transaction>(
|
|||||||
setValidAddresses(tx)
|
setValidAddresses(tx)
|
||||||
|
|
||||||
setTransactionFlagsToNumber(tx)
|
setTransactionFlagsToNumber(tx)
|
||||||
|
|
||||||
const promises: Array<Promise<void>> = []
|
const promises: Array<Promise<void>> = []
|
||||||
|
if (tx.NetworkID == null) {
|
||||||
|
tx.NetworkID = txNeedsNetworkID(this) ? this.networkID : undefined
|
||||||
|
}
|
||||||
if (tx.Sequence == null) {
|
if (tx.Sequence == null) {
|
||||||
promises.push(setNextValidSequenceNumber(this, tx))
|
promises.push(setNextValidSequenceNumber(this, tx))
|
||||||
}
|
}
|
||||||
@@ -88,6 +97,101 @@ async function autofill<T extends Transaction>(
|
|||||||
return Promise.all(promises).then(() => tx)
|
return Promise.all(promises).then(() => tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the source rippled version is not later than the target rippled version.
|
||||||
|
* Example usage: isNotLaterRippledVersion('1.10.0', '1.11.0') returns true.
|
||||||
|
* isNotLaterRippledVersion('1.10.0', '1.10.0-b1') returns false.
|
||||||
|
*
|
||||||
|
* @param source -- The source rippled version.
|
||||||
|
* @param target -- The target rippled version.
|
||||||
|
* @returns True if source is earlier than target, false otherwise.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line max-lines-per-function, max-statements -- Disable for this helper functions.
|
||||||
|
function isNotLaterRippledVersion(source: string, target: string): boolean {
|
||||||
|
if (source === target) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const sourceDecomp = source.split('.')
|
||||||
|
const targetDecomp = target.split('.')
|
||||||
|
const sourceMajor = parseInt(sourceDecomp[0], 10)
|
||||||
|
const sourceMinor = parseInt(sourceDecomp[1], 10)
|
||||||
|
const targetMajor = parseInt(targetDecomp[0], 10)
|
||||||
|
const targetMinor = parseInt(targetDecomp[1], 10)
|
||||||
|
// Compare major version
|
||||||
|
if (sourceMajor !== targetMajor) {
|
||||||
|
return sourceMajor < targetMajor
|
||||||
|
}
|
||||||
|
// Compare minor version
|
||||||
|
if (sourceMinor !== targetMinor) {
|
||||||
|
return sourceMinor < targetMinor
|
||||||
|
}
|
||||||
|
const sourcePatch = sourceDecomp[2].split('-')
|
||||||
|
const targetPatch = targetDecomp[2].split('-')
|
||||||
|
|
||||||
|
const sourcePatchVersion = parseInt(sourcePatch[0], 10)
|
||||||
|
const targetPatchVersion = parseInt(targetPatch[0], 10)
|
||||||
|
|
||||||
|
// Compare patch version
|
||||||
|
if (sourcePatchVersion !== targetPatchVersion) {
|
||||||
|
return sourcePatchVersion < targetPatchVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare release version
|
||||||
|
if (sourcePatch.length !== targetPatch.length) {
|
||||||
|
return sourcePatch.length > targetPatch.length
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourcePatch.length === 2) {
|
||||||
|
// Compare different release types
|
||||||
|
if (!sourcePatch[1][0].startsWith(targetPatch[1][0])) {
|
||||||
|
return sourcePatch[1] < targetPatch[1]
|
||||||
|
}
|
||||||
|
// Compare beta version
|
||||||
|
if (sourcePatch[1].startsWith('b')) {
|
||||||
|
return (
|
||||||
|
parseInt(sourcePatch[1].slice(1), 10) <
|
||||||
|
parseInt(targetPatch[1].slice(1), 10)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Compare rc version
|
||||||
|
return (
|
||||||
|
parseInt(sourcePatch[1].slice(2), 10) <
|
||||||
|
parseInt(targetPatch[1].slice(2), 10)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the transaction required a networkID to be valid.
|
||||||
|
* Transaction needs networkID if later than restricted ID and either the network is hooks testnet
|
||||||
|
* or build version is >= 1.11.0
|
||||||
|
*
|
||||||
|
* @param client -- The connected client.
|
||||||
|
* @returns True if required networkID, false otherwise.
|
||||||
|
*/
|
||||||
|
function txNeedsNetworkID(client: Client): boolean {
|
||||||
|
if (
|
||||||
|
client.networkID !== undefined &&
|
||||||
|
client.networkID > RESTRICTED_NETWORKS
|
||||||
|
) {
|
||||||
|
// TODO: remove the buildVersion logic when 1.11.0 is out and widely used.
|
||||||
|
// Issue: https://github.com/XRPLF/xrpl.js/issues/2339
|
||||||
|
if (
|
||||||
|
(client.buildVersion &&
|
||||||
|
isNotLaterRippledVersion(
|
||||||
|
REQUIRED_NETWORKID_VERSION,
|
||||||
|
client.buildVersion,
|
||||||
|
)) ||
|
||||||
|
client.networkID === HOOKS_TESTNET_ID
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
function setValidAddresses(tx: Transaction): void {
|
function setValidAddresses(tx: Transaction): void {
|
||||||
validateAccountAddress(tx, 'Account', 'SourceTag')
|
validateAccountAddress(tx, 'Account', 'SourceTag')
|
||||||
// eslint-disable-next-line @typescript-eslint/dot-notation -- Destination can exist on Transaction
|
// eslint-disable-next-line @typescript-eslint/dot-notation -- Destination can exist on Transaction
|
||||||
|
|||||||
@@ -15,13 +15,32 @@ import {
|
|||||||
} from '../setupClient'
|
} from '../setupClient'
|
||||||
import { assertRejects } from '../testUtils'
|
import { assertRejects } from '../testUtils'
|
||||||
|
|
||||||
|
const NetworkID = 1025
|
||||||
const Fee = '10'
|
const Fee = '10'
|
||||||
const Sequence = 1432
|
const Sequence = 1432
|
||||||
const LastLedgerSequence = 2908734
|
const LastLedgerSequence = 2908734
|
||||||
|
const HOOKS_TESTNET_ID = 21338
|
||||||
|
|
||||||
describe('client.autofill', function () {
|
describe('client.autofill', function () {
|
||||||
let testContext: XrplTestContext
|
let testContext: XrplTestContext
|
||||||
|
|
||||||
|
async function setupMockRippledVersionAndID(
|
||||||
|
buildVersion: string,
|
||||||
|
networkID: number,
|
||||||
|
): Promise<void> {
|
||||||
|
await testContext.client.disconnect()
|
||||||
|
rippled.server_info.withNetworkId.result.info.build_version = buildVersion
|
||||||
|
rippled.server_info.withNetworkId.result.info.network_id = networkID
|
||||||
|
testContext.client.connection.on('connected', () => {
|
||||||
|
testContext.mockRippled?.addResponse(
|
||||||
|
'server_info',
|
||||||
|
rippled.server_info.withNetworkId,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await testContext.client.connect()
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
testContext = await setupClient()
|
testContext = await setupClient()
|
||||||
})
|
})
|
||||||
@@ -32,17 +51,116 @@ describe('client.autofill', function () {
|
|||||||
TransactionType: 'DepositPreauth',
|
TransactionType: 'DepositPreauth',
|
||||||
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
Account: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||||
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
|
Authorize: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
|
||||||
|
NetworkID,
|
||||||
Fee,
|
Fee,
|
||||||
Sequence,
|
Sequence,
|
||||||
LastLedgerSequence,
|
LastLedgerSequence,
|
||||||
}
|
}
|
||||||
const txResult = await testContext.client.autofill(tx)
|
const txResult = await testContext.client.autofill(tx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.NetworkID, NetworkID)
|
||||||
assert.strictEqual(txResult.Fee, Fee)
|
assert.strictEqual(txResult.Fee, Fee)
|
||||||
assert.strictEqual(txResult.Sequence, Sequence)
|
assert.strictEqual(txResult.Sequence, Sequence)
|
||||||
assert.strictEqual(txResult.LastLedgerSequence, LastLedgerSequence)
|
assert.strictEqual(txResult.LastLedgerSequence, LastLedgerSequence)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('ignores network ID if missing', async function () {
|
||||||
|
const tx: Payment = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
|
||||||
|
Amount: '1234',
|
||||||
|
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
|
||||||
|
Fee,
|
||||||
|
Sequence,
|
||||||
|
LastLedgerSequence,
|
||||||
|
}
|
||||||
|
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
|
||||||
|
|
||||||
|
const txResult = await testContext.client.autofill(tx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.NetworkID, undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
// NetworkID is required in transaction for network > 1024 and from version 1.11.0 or later.
|
||||||
|
// More context: https://github.com/XRPLF/rippled/pull/4370
|
||||||
|
it('overrides network ID if > 1024 and version is later than 1.11.0', async function () {
|
||||||
|
await setupMockRippledVersionAndID('1.11.1', 1025)
|
||||||
|
const tx: Payment = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
|
||||||
|
Amount: '1234',
|
||||||
|
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
|
||||||
|
Fee,
|
||||||
|
Sequence,
|
||||||
|
LastLedgerSequence,
|
||||||
|
}
|
||||||
|
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
|
||||||
|
|
||||||
|
const txResult = await testContext.client.autofill(tx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.NetworkID, 1025)
|
||||||
|
})
|
||||||
|
|
||||||
|
// NetworkID is only required in transaction for version 1.11.0 or later.
|
||||||
|
// More context: https://github.com/XRPLF/rippled/pull/4370
|
||||||
|
it('ignores network ID if > 1024 but version is earlier than 1.11.0', async function () {
|
||||||
|
await setupMockRippledVersionAndID('1.10.0', 1025)
|
||||||
|
const tx: Payment = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
|
||||||
|
Amount: '1234',
|
||||||
|
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
|
||||||
|
Fee,
|
||||||
|
Sequence,
|
||||||
|
LastLedgerSequence,
|
||||||
|
}
|
||||||
|
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
|
||||||
|
|
||||||
|
const txResult = await testContext.client.autofill(tx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.NetworkID, undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
// NetworkID <= 1024 does not require a newtorkID in transaction.
|
||||||
|
// More context: https://github.com/XRPLF/rippled/pull/4370
|
||||||
|
it('ignores network ID if <= 1024', async function () {
|
||||||
|
await setupMockRippledVersionAndID('1.11.1', 1023)
|
||||||
|
const tx: Payment = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
|
||||||
|
Amount: '1234',
|
||||||
|
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
|
||||||
|
Fee,
|
||||||
|
Sequence,
|
||||||
|
LastLedgerSequence,
|
||||||
|
}
|
||||||
|
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
|
||||||
|
|
||||||
|
const txResult = await testContext.client.autofill(tx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.NetworkID, undefined)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Hooks Testnet requires networkID in transaction regardless of version.
|
||||||
|
// More context: https://github.com/XRPLF/rippled/pull/4370
|
||||||
|
it('overrides network ID for hooks testnet', async function () {
|
||||||
|
await setupMockRippledVersionAndID('1.10.1', HOOKS_TESTNET_ID)
|
||||||
|
const tx: Payment = {
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
Account: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
|
||||||
|
Amount: '1234',
|
||||||
|
Destination: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
|
||||||
|
Fee,
|
||||||
|
Sequence,
|
||||||
|
LastLedgerSequence,
|
||||||
|
}
|
||||||
|
testContext.mockRippled!.addResponse('ledger', rippled.ledger.normal)
|
||||||
|
|
||||||
|
const txResult = await testContext.client.autofill(tx)
|
||||||
|
|
||||||
|
assert.strictEqual(txResult.NetworkID, HOOKS_TESTNET_ID)
|
||||||
|
})
|
||||||
|
|
||||||
it('converts Account & Destination X-address to their classic address', async function () {
|
it('converts Account & Destination X-address to their classic address', async function () {
|
||||||
const tx: Payment = {
|
const tx: Payment = {
|
||||||
TransactionType: 'Payment',
|
TransactionType: 'Payment',
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
|
|
||||||
import { destroyServer, getFreePort } from './testUtils'
|
import { destroyServer, getFreePort } from './testUtils'
|
||||||
|
|
||||||
function createResponse(
|
export function createResponse(
|
||||||
request: { id: number | string },
|
request: { id: number | string },
|
||||||
response: Record<string, unknown>,
|
response: Record<string, unknown>,
|
||||||
): string {
|
): string {
|
||||||
|
|||||||
2
packages/xrpl/test/fixtures/rippled/index.ts
vendored
2
packages/xrpl/test/fixtures/rippled/index.ts
vendored
@@ -12,6 +12,7 @@ import iouPartialPayment from './partialPaymentIOU.json'
|
|||||||
import xrpPartialPayment from './partialPaymentXRP.json'
|
import xrpPartialPayment from './partialPaymentXRP.json'
|
||||||
import normalServerInfo from './serverInfo.json'
|
import normalServerInfo from './serverInfo.json'
|
||||||
import highLoadFactor from './serverInfoHighLoadFactor.json'
|
import highLoadFactor from './serverInfoHighLoadFactor.json'
|
||||||
|
import withNetworkIDServerInfo from './serverInfoNetworkID.json'
|
||||||
import consensusStream from './streams/consensusPhase.json'
|
import consensusStream from './streams/consensusPhase.json'
|
||||||
import ledgerStream from './streams/ledger.json'
|
import ledgerStream from './streams/ledger.json'
|
||||||
import manifestStream from './streams/manifest.json'
|
import manifestStream from './streams/manifest.json'
|
||||||
@@ -84,6 +85,7 @@ const ledger_data = {
|
|||||||
const server_info = {
|
const server_info = {
|
||||||
normal: normalServerInfo,
|
normal: normalServerInfo,
|
||||||
highLoadFactor,
|
highLoadFactor,
|
||||||
|
withNetworkId: withNetworkIDServerInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
const tx = {
|
const tx = {
|
||||||
|
|||||||
31
packages/xrpl/test/fixtures/rippled/serverInfoNetworkID.json
vendored
Normal file
31
packages/xrpl/test/fixtures/rippled/serverInfoNetworkID.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"status": "success",
|
||||||
|
"type": "response",
|
||||||
|
"result": {
|
||||||
|
"info": {
|
||||||
|
"build_version": "1.11.0-rc2",
|
||||||
|
"complete_ledgers": "37621036-38327626",
|
||||||
|
"hostid": "JANE",
|
||||||
|
"io_latency_ms": 1,
|
||||||
|
"last_close": {
|
||||||
|
"converge_time_s": 2,
|
||||||
|
"proposers": 6
|
||||||
|
},
|
||||||
|
"load_factor": 1,
|
||||||
|
"network_id": 1,
|
||||||
|
"peers": 113,
|
||||||
|
"pubkey_node": "n9L6MAkAvZKakewLSJPkCKLxuSQ9jrYXJBd2L4fouhpXauyFh6ZM",
|
||||||
|
"server_state": "full",
|
||||||
|
"validated_ledger": {
|
||||||
|
"age": 0,
|
||||||
|
"base_fee_xrp": 0.00001,
|
||||||
|
"hash": "A219F66BB8C9992E80A3C93A5EA408CD54B8F47F2AC1246C271C495F833752BA",
|
||||||
|
"reserve_base_xrp": 10,
|
||||||
|
"reserve_inc_xrp": 2,
|
||||||
|
"seq": 38327626
|
||||||
|
},
|
||||||
|
"validation_quorum": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ describe('mock rippled tests', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await assertRejects(
|
await assertRejects(
|
||||||
testContext.client.request({ command: 'server_info' }),
|
testContext.client.request({ command: 'account_info' }),
|
||||||
RippledError,
|
RippledError,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -232,4 +232,17 @@ describe('BaseTransaction', function () {
|
|||||||
'BaseTransaction: invalid Memos',
|
'BaseTransaction: invalid Memos',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it(`Handles invalid NetworkID`, function () {
|
||||||
|
const invalidNetworkID = {
|
||||||
|
Account: 'r97KeayHuEsDwyU1yPBVtMLLoQr79QcRFe',
|
||||||
|
TransactionType: 'Payment',
|
||||||
|
NetworkID: '1024',
|
||||||
|
}
|
||||||
|
assert.throws(
|
||||||
|
() => validateBaseTransaction(invalidNetworkID),
|
||||||
|
ValidationError,
|
||||||
|
'BaseTransaction: invalid NetworkID',
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import BroadcastClient from '../src/client/BroadcastClient'
|
|||||||
import createMockRippled, {
|
import createMockRippled, {
|
||||||
type MockedWebSocketServer,
|
type MockedWebSocketServer,
|
||||||
} from './createMockRippled'
|
} from './createMockRippled'
|
||||||
|
import rippled from './fixtures/rippled'
|
||||||
import { destroyServer, getFreePort } from './testUtils'
|
import { destroyServer, getFreePort } from './testUtils'
|
||||||
|
|
||||||
export interface XrplTestContext {
|
export interface XrplTestContext {
|
||||||
@@ -29,6 +30,10 @@ async function setupMockRippledConnection(
|
|||||||
context.client.on('error', () => {
|
context.client.on('error', () => {
|
||||||
// We must have an error listener attached for reconnect errors
|
// We must have an error listener attached for reconnect errors
|
||||||
})
|
})
|
||||||
|
context.mockRippled?.addResponse(
|
||||||
|
'server_info',
|
||||||
|
rippled.server_info.withNetworkId,
|
||||||
|
)
|
||||||
|
|
||||||
return context.client.connect().then(() => context)
|
return context.client.connect().then(() => context)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user