mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 13:05:49 +00:00
test: add integration tests for sidechain transactions (#2301)
* add XChainCreateBridge integration test * add XChainCreateClaimID integration test * add XChainCommit integration test * add XChainAccountCreateCommit integration test * simplify tests * add XChainModifyBridge integration test * add XChainAddAccountCreateAttestation integration test * add XChainAddClaimAttestation integration test * improve SignerListSet integration test * rename variable to match * add XChainClaim integration test * clean up * add IOU attestation test * fix integration tests issues * minor refactors * fix bug * [WIP] switch to one bridge for all integration tests * clean up * fix tests * fix + clean up tests
This commit is contained in:
@@ -67,20 +67,20 @@ class XChainBridge extends SerializedType {
|
||||
return value
|
||||
}
|
||||
|
||||
if (isXChainBridgeObject(value)) {
|
||||
const bytes: Array<Buffer> = []
|
||||
this.TYPE_ORDER.forEach((item) => {
|
||||
const { name, type } = item
|
||||
if (type === AccountID) {
|
||||
bytes.push(Buffer.from([0x14]))
|
||||
}
|
||||
const object = type.from(value[name])
|
||||
bytes.push(object.toBytes())
|
||||
})
|
||||
return new XChainBridge(Buffer.concat(bytes))
|
||||
if (!isXChainBridgeObject(value)) {
|
||||
throw new Error('Invalid type to construct an XChainBridge')
|
||||
}
|
||||
|
||||
throw new Error('Invalid type to construct an XChainBridge')
|
||||
const bytes: Array<Buffer> = []
|
||||
this.TYPE_ORDER.forEach((item) => {
|
||||
const { name, type } = item
|
||||
if (type === AccountID) {
|
||||
bytes.push(Buffer.from([0x14]))
|
||||
}
|
||||
const object = type.from(value[name])
|
||||
bytes.push(object.toBytes())
|
||||
})
|
||||
return new XChainBridge(Buffer.concat(bytes))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,13 +22,6 @@ export default interface Bridge extends BaseLedgerEntry, HasPreviousTxnID {
|
||||
*/
|
||||
SignatureReward: Amount
|
||||
|
||||
/**
|
||||
* The minimum amount, in XRP, required for an {@link XChainAccountCreateCommit}
|
||||
* transaction. If this isn't present, the {@link XChainAccountCreateCommit}
|
||||
* transaction will fail. This field can only be present on XRP-XRP bridges.
|
||||
*/
|
||||
MinAccountCreateAmount?: string
|
||||
|
||||
/**
|
||||
* The door accounts and assets of the bridge this object correlates to.
|
||||
*/
|
||||
@@ -58,6 +51,13 @@ export default interface Bridge extends BaseLedgerEntry, HasPreviousTxnID {
|
||||
*/
|
||||
XChainAccountClaimCount: string
|
||||
|
||||
/**
|
||||
* The minimum amount, in XRP, required for an {@link XChainAccountCreateCommit}
|
||||
* transaction. If this isn't present, the {@link XChainAccountCreateCommit}
|
||||
* transaction will fail. This field can only be present on XRP-XRP bridges.
|
||||
*/
|
||||
MinAccountCreateAmount?: string
|
||||
|
||||
/**
|
||||
* A bit-map of boolean flags. No flags are defined for Bridges, so this value
|
||||
* is always 0.
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
* The XChainAccountCreateCommit transaction creates a new account on one of the
|
||||
* chains a bridge connects, which serves as the bridge entrance for that chain.
|
||||
*
|
||||
* Warning: This transaction should only be executed if the witness attestations
|
||||
* WARNING: This transaction should only be executed if the witness attestations
|
||||
* will be reliably delivered to the destination chain. If the signatures aren't
|
||||
* delivered, then account creation will be blocked until attestations are received.
|
||||
* This can be used maliciously; to disable this transaction on XRP-XRP bridges,
|
||||
|
||||
@@ -21,7 +21,7 @@ async function generate_faucet_wallet_and_fund_again(
|
||||
faucetPath,
|
||||
usageContext: 'integration-test',
|
||||
})
|
||||
assert.notEqual(wallet, undefined)
|
||||
assert.notStrictEqual(wallet, undefined)
|
||||
assert(isValidClassicAddress(wallet.classicAddress))
|
||||
assert(isValidXAddress(wallet.getXAddress()))
|
||||
|
||||
@@ -103,7 +103,7 @@ describe('fundWallet', function () {
|
||||
usageContext: 'integration-test',
|
||||
})
|
||||
|
||||
assert.notEqual(wallet, undefined)
|
||||
assert.notStrictEqual(wallet, undefined)
|
||||
assert(isValidClassicAddress(wallet.classicAddress))
|
||||
assert(isValidXAddress(wallet.getXAddress()))
|
||||
|
||||
@@ -137,7 +137,7 @@ describe('fundWallet', function () {
|
||||
usageContext: 'integration-test',
|
||||
})
|
||||
assert.equal(balance, '2000')
|
||||
assert.notEqual(wallet, undefined)
|
||||
assert.notStrictEqual(wallet, undefined)
|
||||
assert(isValidClassicAddress(wallet.classicAddress))
|
||||
assert(isValidXAddress(wallet.getXAddress()))
|
||||
|
||||
|
||||
@@ -19,6 +19,14 @@ export * from './transactions/paymentChannelCreate.test'
|
||||
export * from './transactions/paymentChannelFund.test'
|
||||
export * from './transactions/signerListSet.test'
|
||||
export * from './transactions/trustSet.test'
|
||||
export * from './transactions/xchainAccountCreateCommit.test'
|
||||
export * from './transactions/xchainAddAccountCreateAttestation.test'
|
||||
export * from './transactions/xchainAddClaimAttestation.test'
|
||||
export * from './transactions/xchainClaim.test'
|
||||
export * from './transactions/xchainCreateBridge.test'
|
||||
export * from './transactions/xchainCreateClaimID.test'
|
||||
export * from './transactions/xchainCommit.test'
|
||||
export * from './transactions/xchainModifyBridge.test'
|
||||
|
||||
// Requests
|
||||
export * from './requests/accountChannels.test'
|
||||
|
||||
@@ -1,7 +1,26 @@
|
||||
import { Client, Wallet } from '../../src'
|
||||
import { assert } from 'chai'
|
||||
|
||||
import {
|
||||
Client,
|
||||
SignerListSet,
|
||||
Wallet,
|
||||
XChainBridge,
|
||||
XChainCreateBridge,
|
||||
} from '../../src'
|
||||
|
||||
import serverUrl from './serverUrl'
|
||||
import { fundAccount } from './utils'
|
||||
import {
|
||||
GENESIS_ACCOUNT,
|
||||
fundAccount,
|
||||
generateFundedWallet,
|
||||
testTransaction,
|
||||
} from './utils'
|
||||
|
||||
interface TestBridge {
|
||||
xchainBridge: XChainBridge
|
||||
witness: Wallet
|
||||
signatureReward: string
|
||||
}
|
||||
|
||||
export interface XrplIntegrationTestContext {
|
||||
client: Client
|
||||
@@ -32,15 +51,86 @@ async function connectWithRetry(client: Client, tries = 0): Promise<void> {
|
||||
export async function setupClient(
|
||||
server = serverUrl,
|
||||
): Promise<XrplIntegrationTestContext> {
|
||||
const context: XrplIntegrationTestContext = {
|
||||
client: new Client(server, { timeout: 200000 }),
|
||||
wallet: Wallet.generate(),
|
||||
}
|
||||
return connectWithRetry(context.client).then(async () => {
|
||||
await fundAccount(context.client, context.wallet, {
|
||||
const client = new Client(server, { timeout: 200000 })
|
||||
const wallet = Wallet.generate()
|
||||
return connectWithRetry(client).then(async () => {
|
||||
await fundAccount(client, wallet, {
|
||||
count: 20,
|
||||
delayMs: 1000,
|
||||
})
|
||||
const context: XrplIntegrationTestContext = {
|
||||
client,
|
||||
wallet,
|
||||
}
|
||||
return context
|
||||
})
|
||||
}
|
||||
|
||||
export async function setupBridge(client: Client): Promise<TestBridge> {
|
||||
const doorAccount = await generateFundedWallet(client)
|
||||
const signatureReward = '200'
|
||||
const xchainBridge: XChainBridge = {
|
||||
LockingChainDoor: doorAccount.classicAddress,
|
||||
LockingChainIssue: { currency: 'XRP' },
|
||||
IssuingChainDoor: GENESIS_ACCOUNT,
|
||||
IssuingChainIssue: { currency: 'XRP' },
|
||||
}
|
||||
const setupTx: XChainCreateBridge = {
|
||||
TransactionType: 'XChainCreateBridge',
|
||||
Account: doorAccount.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
SignatureReward: signatureReward,
|
||||
MinAccountCreateAmount: '10000000',
|
||||
}
|
||||
|
||||
await testTransaction(client, setupTx, doorAccount)
|
||||
|
||||
// confirm that the transaction actually went through
|
||||
const accountObjectsResponse = await client.request({
|
||||
command: 'account_objects',
|
||||
account: doorAccount.classicAddress,
|
||||
type: 'bridge',
|
||||
})
|
||||
assert.lengthOf(
|
||||
accountObjectsResponse.result.account_objects,
|
||||
1,
|
||||
'Should be exactly one bridge owned by the account',
|
||||
)
|
||||
|
||||
const witnessWallet = await generateFundedWallet(client)
|
||||
|
||||
const signerTx: SignerListSet = {
|
||||
TransactionType: 'SignerListSet',
|
||||
Account: doorAccount.classicAddress,
|
||||
SignerEntries: [
|
||||
{
|
||||
SignerEntry: {
|
||||
Account: witnessWallet.classicAddress,
|
||||
SignerWeight: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
SignerQuorum: 1,
|
||||
}
|
||||
await testTransaction(client, signerTx, doorAccount)
|
||||
|
||||
const signerAccountInfoResponse = await client.request({
|
||||
command: 'account_info',
|
||||
account: doorAccount.classicAddress,
|
||||
signer_lists: true,
|
||||
})
|
||||
const signerListInfo =
|
||||
signerAccountInfoResponse.result.account_data.signer_lists?.[0]
|
||||
assert.deepEqual(
|
||||
signerListInfo?.SignerEntries,
|
||||
signerTx.SignerEntries,
|
||||
'SignerEntries were not set properly',
|
||||
)
|
||||
assert.equal(
|
||||
signerListInfo?.SignerQuorum,
|
||||
signerTx.SignerQuorum,
|
||||
'SignerQuorum was not set properly',
|
||||
)
|
||||
|
||||
return { xchainBridge, witness: witnessWallet, signatureReward }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { SignerListSet } from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
@@ -42,6 +44,24 @@ describe('SignerListSet', function () {
|
||||
SignerQuorum: 2,
|
||||
}
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
|
||||
const accountInfoResponse = await testContext.client.request({
|
||||
command: 'account_info',
|
||||
account: testContext.wallet.classicAddress,
|
||||
signer_lists: true,
|
||||
})
|
||||
const signerListInfo =
|
||||
accountInfoResponse.result.account_data.signer_lists?.[0]
|
||||
assert.deepEqual(
|
||||
signerListInfo?.SignerEntries,
|
||||
tx.SignerEntries,
|
||||
'SignerEntries were not set properly',
|
||||
)
|
||||
assert.equal(
|
||||
signerListInfo?.SignerQuorum,
|
||||
tx.SignerQuorum,
|
||||
'SignerQuorum was not set properly',
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { XChainAccountCreateCommit, Wallet } from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
setupBridge,
|
||||
} from '../setup'
|
||||
import { generateFundedWallet, getXRPBalance, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainAccountCreateCommit', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const { xchainBridge, signatureReward } = await setupBridge(
|
||||
testContext.client,
|
||||
)
|
||||
const initialBalance = Number(
|
||||
await getXRPBalance(testContext.client, xchainBridge.LockingChainDoor),
|
||||
)
|
||||
|
||||
// actually test XChainAccountCreateCommit
|
||||
const wallet2 = await generateFundedWallet(testContext.client)
|
||||
const destination = Wallet.generate()
|
||||
const amount = 10000000
|
||||
const tx: XChainAccountCreateCommit = {
|
||||
TransactionType: 'XChainAccountCreateCommit',
|
||||
Account: wallet2.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
Amount: amount.toString(),
|
||||
SignatureReward: signatureReward,
|
||||
Destination: destination.classicAddress,
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, wallet2)
|
||||
|
||||
const accountInfoResponse2 = await testContext.client.request({
|
||||
command: 'account_info',
|
||||
account: xchainBridge.LockingChainDoor,
|
||||
})
|
||||
const finalBalance = Number(
|
||||
accountInfoResponse2.result.account_data.Balance,
|
||||
)
|
||||
assert.equal(
|
||||
finalBalance,
|
||||
initialBalance + amount + Number(signatureReward),
|
||||
"The bridge door's balance should go up by the amount committed",
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,76 @@
|
||||
import { encode } from 'ripple-binary-codec'
|
||||
import { sign } from 'ripple-keypairs'
|
||||
|
||||
import {
|
||||
Wallet,
|
||||
XChainAddAccountCreateAttestation,
|
||||
xrpToDrops,
|
||||
} from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupBridge,
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCreateBridge', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const { xchainBridge, witness, signatureReward } = await setupBridge(
|
||||
testContext.client,
|
||||
)
|
||||
const destination = Wallet.generate()
|
||||
const otherChainSource = Wallet.generate()
|
||||
|
||||
const attestationToSign = {
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: xrpToDrops(300),
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
WasLockingChainSend: 0,
|
||||
XChainAccountCreateCount: 1,
|
||||
Destination: destination.classicAddress,
|
||||
SignatureReward: signatureReward,
|
||||
}
|
||||
const encodedAttestation = encode(attestationToSign)
|
||||
const attestationSignature = sign(encodedAttestation, witness.privateKey)
|
||||
|
||||
const tx: XChainAddAccountCreateAttestation = {
|
||||
TransactionType: 'XChainAddAccountCreateAttestation',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: xrpToDrops(300),
|
||||
WasLockingChainSend: 0,
|
||||
XChainAccountCreateCount: 1,
|
||||
Destination: destination.classicAddress,
|
||||
SignatureReward: signatureReward,
|
||||
PublicKey: witness.publicKey,
|
||||
Signature: attestationSignature,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
AttestationSignerAccount: witness.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
|
||||
// should not throw
|
||||
await testContext.client.request({
|
||||
command: 'account_info',
|
||||
account: destination.classicAddress,
|
||||
})
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,274 @@
|
||||
/* eslint-disable max-statements -- necessary because transfers require a lot of steps */
|
||||
import { assert } from 'chai'
|
||||
import { encode } from 'ripple-binary-codec'
|
||||
import { sign } from 'ripple-keypairs'
|
||||
|
||||
import {
|
||||
AccountSet,
|
||||
AccountSetAsfFlags,
|
||||
IssuedCurrency,
|
||||
IssuedCurrencyAmount,
|
||||
SignerListSet,
|
||||
TrustSet,
|
||||
Wallet,
|
||||
XChainAddClaimAttestation,
|
||||
XChainBridge,
|
||||
XChainCreateBridge,
|
||||
XChainCreateClaimID,
|
||||
xrpToDrops,
|
||||
} from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupBridge,
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import {
|
||||
generateFundedWallet,
|
||||
getIOUBalance,
|
||||
getXRPBalance,
|
||||
testTransaction,
|
||||
} from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCreateBridge', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const { xchainBridge, witness, signatureReward } = await setupBridge(
|
||||
testContext.client,
|
||||
)
|
||||
const otherChainSource = Wallet.generate()
|
||||
const amount = xrpToDrops(10)
|
||||
|
||||
const claimIdTx: XChainCreateClaimID = {
|
||||
TransactionType: 'XChainCreateClaimID',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
SignatureReward: signatureReward,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, claimIdTx, testContext.wallet)
|
||||
|
||||
const initialBalance = Number(
|
||||
await getXRPBalance(testContext.client, testContext.wallet),
|
||||
)
|
||||
|
||||
const attestationToSign = {
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: amount,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
WasLockingChainSend: 0,
|
||||
XChainClaimID: 1,
|
||||
Destination: testContext.wallet.classicAddress,
|
||||
}
|
||||
const encodedAttestation = encode(attestationToSign)
|
||||
const attestationSignature = sign(encodedAttestation, witness.privateKey)
|
||||
|
||||
const tx: XChainAddClaimAttestation = {
|
||||
TransactionType: 'XChainAddClaimAttestation',
|
||||
Account: witness.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: amount,
|
||||
WasLockingChainSend: 0,
|
||||
XChainClaimID: 1,
|
||||
Destination: testContext.wallet.classicAddress,
|
||||
PublicKey: witness.publicKey,
|
||||
Signature: attestationSignature,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
AttestationSignerAccount: witness.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, tx, witness)
|
||||
|
||||
const finalBalance = Number(
|
||||
await getXRPBalance(testContext.client, testContext.wallet),
|
||||
)
|
||||
assert.equal(
|
||||
finalBalance,
|
||||
initialBalance + Number(amount) - Number(signatureReward),
|
||||
'The destination balance should go up by the amount transferred',
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
|
||||
it(
|
||||
'IOU',
|
||||
async () => {
|
||||
const witness = await generateFundedWallet(testContext.client)
|
||||
// we are on the "issuing chain" for this test
|
||||
const lockingDoor = Wallet.generate()
|
||||
const issuer = Wallet.generate()
|
||||
|
||||
// set default rippling
|
||||
const defaultRipplingTx: AccountSet = {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
SetFlag: AccountSetAsfFlags.asfDefaultRipple,
|
||||
}
|
||||
await testTransaction(
|
||||
testContext.client,
|
||||
defaultRipplingTx,
|
||||
testContext.wallet,
|
||||
)
|
||||
|
||||
const destination = await generateFundedWallet(testContext.client)
|
||||
|
||||
const trustlineTx: TrustSet = {
|
||||
TransactionType: 'TrustSet',
|
||||
Account: destination.classicAddress,
|
||||
LimitAmount: {
|
||||
currency: 'USD',
|
||||
issuer: testContext.wallet.classicAddress,
|
||||
value: '1000000000',
|
||||
},
|
||||
}
|
||||
await testTransaction(testContext.client, trustlineTx, destination)
|
||||
|
||||
const signatureReward = '200'
|
||||
const xchainBridge: XChainBridge = {
|
||||
LockingChainDoor: lockingDoor.classicAddress,
|
||||
LockingChainIssue: {
|
||||
currency: 'USD',
|
||||
issuer: issuer.classicAddress,
|
||||
},
|
||||
IssuingChainDoor: testContext.wallet.classicAddress,
|
||||
IssuingChainIssue: {
|
||||
currency: 'USD',
|
||||
issuer: testContext.wallet.classicAddress,
|
||||
},
|
||||
}
|
||||
const setupTx: XChainCreateBridge = {
|
||||
TransactionType: 'XChainCreateBridge',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
SignatureReward: signatureReward,
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, setupTx, testContext.wallet)
|
||||
|
||||
// confirm that the transaction actually went through
|
||||
const accountObjectsResponse = await testContext.client.request({
|
||||
command: 'account_objects',
|
||||
account: testContext.wallet.classicAddress,
|
||||
type: 'bridge',
|
||||
})
|
||||
assert.lengthOf(
|
||||
accountObjectsResponse.result.account_objects,
|
||||
1,
|
||||
'Should be exactly one bridge owned by the account',
|
||||
)
|
||||
|
||||
const signerTx: SignerListSet = {
|
||||
TransactionType: 'SignerListSet',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
SignerEntries: [
|
||||
{
|
||||
SignerEntry: {
|
||||
Account: witness.classicAddress,
|
||||
SignerWeight: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
SignerQuorum: 1,
|
||||
}
|
||||
await testTransaction(testContext.client, signerTx, testContext.wallet)
|
||||
|
||||
const signerAccountInfoResponse = await testContext.client.request({
|
||||
command: 'account_info',
|
||||
account: testContext.wallet.classicAddress,
|
||||
signer_lists: true,
|
||||
})
|
||||
const signerListInfo =
|
||||
signerAccountInfoResponse.result.account_data.signer_lists?.[0]
|
||||
assert.deepEqual(
|
||||
signerListInfo?.SignerEntries,
|
||||
signerTx.SignerEntries,
|
||||
'SignerEntries were not set properly',
|
||||
)
|
||||
assert.equal(
|
||||
signerListInfo?.SignerQuorum,
|
||||
signerTx.SignerQuorum,
|
||||
'SignerQuorum was not set properly',
|
||||
)
|
||||
|
||||
const otherChainSource = Wallet.generate()
|
||||
const amount: IssuedCurrencyAmount = {
|
||||
currency: 'USD',
|
||||
issuer: issuer.classicAddress,
|
||||
value: '10',
|
||||
}
|
||||
|
||||
const claimIdTx: XChainCreateClaimID = {
|
||||
TransactionType: 'XChainCreateClaimID',
|
||||
Account: destination.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
SignatureReward: signatureReward,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, claimIdTx, destination)
|
||||
|
||||
const initialBalance = Number(
|
||||
await getIOUBalance(
|
||||
testContext.client,
|
||||
destination,
|
||||
xchainBridge.IssuingChainIssue as IssuedCurrency,
|
||||
),
|
||||
)
|
||||
|
||||
const attestationToSign = {
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: amount,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
WasLockingChainSend: 1,
|
||||
XChainClaimID: 1,
|
||||
Destination: destination.classicAddress,
|
||||
}
|
||||
const encodedAttestation = encode(attestationToSign)
|
||||
const attestationSignature = sign(encodedAttestation, witness.privateKey)
|
||||
|
||||
const tx: XChainAddClaimAttestation = {
|
||||
TransactionType: 'XChainAddClaimAttestation',
|
||||
Account: witness.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: amount,
|
||||
WasLockingChainSend: 1,
|
||||
XChainClaimID: 1,
|
||||
Destination: destination.classicAddress,
|
||||
PublicKey: witness.publicKey,
|
||||
Signature: attestationSignature,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
AttestationSignerAccount: witness.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, tx, witness)
|
||||
|
||||
const finalBalance = Number(
|
||||
await getIOUBalance(
|
||||
testContext.client,
|
||||
destination,
|
||||
xchainBridge.IssuingChainIssue as IssuedCurrency,
|
||||
),
|
||||
)
|
||||
assert.equal(
|
||||
finalBalance,
|
||||
initialBalance + Number(amount.value),
|
||||
'The destination balance should go up by the amount transferred',
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
112
packages/xrpl/test/integration/transactions/xchainClaim.test.ts
Normal file
112
packages/xrpl/test/integration/transactions/xchainClaim.test.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { assert } from 'chai'
|
||||
import { encode } from 'ripple-binary-codec'
|
||||
import { sign } from 'ripple-keypairs'
|
||||
|
||||
import {
|
||||
Wallet,
|
||||
XChainAddClaimAttestation,
|
||||
XChainClaim,
|
||||
XChainCreateClaimID,
|
||||
xrpToDrops,
|
||||
} from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupBridge,
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { generateFundedWallet, getXRPBalance, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCreateBridge', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const { xchainBridge, witness, signatureReward } = await setupBridge(
|
||||
testContext.client,
|
||||
)
|
||||
|
||||
const destination = await generateFundedWallet(testContext.client)
|
||||
const otherChainSource = Wallet.generate()
|
||||
const amount = xrpToDrops(10)
|
||||
|
||||
const claimIdTx: XChainCreateClaimID = {
|
||||
TransactionType: 'XChainCreateClaimID',
|
||||
Account: destination.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
SignatureReward: signatureReward,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, claimIdTx, destination)
|
||||
|
||||
const initialBalance = Number(
|
||||
await getXRPBalance(testContext.client, destination),
|
||||
)
|
||||
|
||||
const attestationToSign = {
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: amount,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
WasLockingChainSend: 0,
|
||||
XChainClaimID: 1,
|
||||
}
|
||||
const encodedAttestation = encode(attestationToSign)
|
||||
const attestationSignature = sign(encodedAttestation, witness.privateKey)
|
||||
|
||||
const claimTx: XChainAddClaimAttestation = {
|
||||
TransactionType: 'XChainAddClaimAttestation',
|
||||
Account: witness.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
Amount: amount,
|
||||
WasLockingChainSend: 0,
|
||||
XChainClaimID: 1,
|
||||
PublicKey: witness.publicKey,
|
||||
Signature: attestationSignature,
|
||||
AttestationRewardAccount: witness.classicAddress,
|
||||
AttestationSignerAccount: witness.classicAddress,
|
||||
}
|
||||
await testTransaction(testContext.client, claimTx, witness)
|
||||
|
||||
const intermediateBalance = Number(
|
||||
await getXRPBalance(testContext.client, destination),
|
||||
)
|
||||
assert.equal(
|
||||
initialBalance,
|
||||
intermediateBalance,
|
||||
"The destination's balance should not change yet",
|
||||
)
|
||||
|
||||
const tx: XChainClaim = {
|
||||
TransactionType: 'XChainClaim',
|
||||
Account: destination.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
Destination: destination.classicAddress,
|
||||
XChainClaimID: 1,
|
||||
Amount: amount,
|
||||
}
|
||||
await testTransaction(testContext.client, tx, destination)
|
||||
|
||||
const finalBalance = Number(
|
||||
await getXRPBalance(testContext.client, destination),
|
||||
)
|
||||
assert.equal(
|
||||
finalBalance,
|
||||
initialBalance + Number(amount) - Number(signatureReward) - 12,
|
||||
"The destination's balance should not change yet",
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { XChainCommit } from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupBridge,
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { generateFundedWallet, getXRPBalance, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCommit', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const { xchainBridge } = await setupBridge(testContext.client)
|
||||
|
||||
const initialBalance = Number(
|
||||
await getXRPBalance(testContext.client, xchainBridge.LockingChainDoor),
|
||||
)
|
||||
|
||||
// actually test XChainCommit
|
||||
const wallet2 = await generateFundedWallet(testContext.client)
|
||||
const amount = 10000000
|
||||
const tx: XChainCommit = {
|
||||
TransactionType: 'XChainCommit',
|
||||
Account: wallet2.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
XChainClaimID: 1,
|
||||
Amount: amount.toString(),
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, wallet2)
|
||||
|
||||
const accountInfoResponse2 = await testContext.client.request({
|
||||
command: 'account_info',
|
||||
account: xchainBridge.LockingChainDoor,
|
||||
})
|
||||
const finalBalance = Number(
|
||||
accountInfoResponse2.result.account_data.Balance,
|
||||
)
|
||||
assert.equal(
|
||||
initialBalance + amount,
|
||||
finalBalance,
|
||||
"The bridge door's balance should go up by the amount committed",
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,55 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { XChainCreateBridge } from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { GENESIS_ACCOUNT, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCreateBridge', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const tx: XChainCreateBridge = {
|
||||
TransactionType: 'XChainCreateBridge',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
XChainBridge: {
|
||||
LockingChainDoor: testContext.wallet.classicAddress,
|
||||
LockingChainIssue: { currency: 'XRP' },
|
||||
IssuingChainDoor: GENESIS_ACCOUNT,
|
||||
IssuingChainIssue: { currency: 'XRP' },
|
||||
},
|
||||
SignatureReward: '200',
|
||||
MinAccountCreateAmount: '10000000',
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
|
||||
// confirm that the transaction actually went through
|
||||
const accountObjectsResponse = await testContext.client.request({
|
||||
command: 'account_objects',
|
||||
account: testContext.wallet.classicAddress,
|
||||
type: 'bridge',
|
||||
})
|
||||
assert.lengthOf(
|
||||
accountObjectsResponse.result.account_objects,
|
||||
1,
|
||||
'Should be exactly one bridge owned by the account',
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { XChainCreateClaimID, Wallet } from '../../../src'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupBridge,
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { generateFundedWallet, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCreateClaimID', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const { xchainBridge, signatureReward } = await setupBridge(
|
||||
testContext.client,
|
||||
)
|
||||
|
||||
// actually test XChainCreateClaimID
|
||||
const wallet2 = await generateFundedWallet(testContext.client)
|
||||
const otherChainSource = Wallet.generate()
|
||||
const tx: XChainCreateClaimID = {
|
||||
TransactionType: 'XChainCreateClaimID',
|
||||
Account: wallet2.classicAddress,
|
||||
XChainBridge: xchainBridge,
|
||||
SignatureReward: signatureReward,
|
||||
OtherChainSource: otherChainSource.classicAddress,
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, tx, wallet2)
|
||||
|
||||
const accountObjectsResponse2 = await testContext.client.request({
|
||||
command: 'account_objects',
|
||||
account: wallet2.classicAddress,
|
||||
type: 'xchain_owned_claim_id',
|
||||
})
|
||||
assert.lengthOf(
|
||||
accountObjectsResponse2.result.account_objects,
|
||||
1,
|
||||
'Should be exactly one claim ID owned by the account',
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,95 @@
|
||||
import { assert } from 'chai'
|
||||
|
||||
import { XChainCreateBridge, XChainModifyBridge } from '../../../src'
|
||||
import { Bridge } from '../../../src/models/ledger'
|
||||
import serverUrl from '../serverUrl'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
type XrplIntegrationTestContext,
|
||||
} from '../setup'
|
||||
import { GENESIS_ACCOUNT, testTransaction } from '../utils'
|
||||
|
||||
// how long before each test case times out
|
||||
const TIMEOUT = 20000
|
||||
|
||||
describe('XChainCreateBridge', function () {
|
||||
let testContext: XrplIntegrationTestContext
|
||||
|
||||
beforeEach(async () => {
|
||||
testContext = await setupClient(serverUrl)
|
||||
})
|
||||
afterEach(async () => teardownClient(testContext))
|
||||
|
||||
it(
|
||||
'base',
|
||||
async () => {
|
||||
const setupTx: XChainCreateBridge = {
|
||||
TransactionType: 'XChainCreateBridge',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
XChainBridge: {
|
||||
LockingChainDoor: testContext.wallet.classicAddress,
|
||||
LockingChainIssue: { currency: 'XRP' },
|
||||
IssuingChainDoor: GENESIS_ACCOUNT,
|
||||
IssuingChainIssue: { currency: 'XRP' },
|
||||
},
|
||||
SignatureReward: '200',
|
||||
MinAccountCreateAmount: '10000000',
|
||||
}
|
||||
|
||||
await testTransaction(testContext.client, setupTx, testContext.wallet)
|
||||
|
||||
// confirm that the transaction actually went through
|
||||
const accountObjectsResponse = await testContext.client.request({
|
||||
command: 'account_objects',
|
||||
account: testContext.wallet.classicAddress,
|
||||
type: 'bridge',
|
||||
})
|
||||
assert.lengthOf(
|
||||
accountObjectsResponse.result.account_objects,
|
||||
1,
|
||||
'Should be exactly one bridge owned by the account',
|
||||
)
|
||||
const initialBridge = accountObjectsResponse.result
|
||||
.account_objects[0] as unknown as Bridge
|
||||
assert.equal(
|
||||
initialBridge.SignatureReward,
|
||||
'200',
|
||||
'Signature reward is incorrect',
|
||||
)
|
||||
|
||||
const tx: XChainModifyBridge = {
|
||||
TransactionType: 'XChainModifyBridge',
|
||||
Account: testContext.wallet.classicAddress,
|
||||
XChainBridge: {
|
||||
LockingChainDoor: testContext.wallet.classicAddress,
|
||||
LockingChainIssue: { currency: 'XRP' },
|
||||
IssuingChainDoor: GENESIS_ACCOUNT,
|
||||
IssuingChainIssue: { currency: 'XRP' },
|
||||
},
|
||||
SignatureReward: '300',
|
||||
}
|
||||
await testTransaction(testContext.client, tx, testContext.wallet)
|
||||
|
||||
// confirm that the transaction actually went through
|
||||
const accountObjectsResponse2 = await testContext.client.request({
|
||||
command: 'account_objects',
|
||||
account: testContext.wallet.classicAddress,
|
||||
type: 'bridge',
|
||||
})
|
||||
assert.lengthOf(
|
||||
accountObjectsResponse2.result.account_objects,
|
||||
1,
|
||||
'Should be exactly one bridge owned by the account',
|
||||
)
|
||||
const finalBridge = accountObjectsResponse2.result
|
||||
.account_objects[0] as unknown as Bridge
|
||||
assert.equal(
|
||||
finalBridge.SignatureReward,
|
||||
'300',
|
||||
'Signature reward was not modified',
|
||||
)
|
||||
},
|
||||
TIMEOUT,
|
||||
)
|
||||
})
|
||||
@@ -10,12 +10,14 @@ import {
|
||||
type SubmitResponse,
|
||||
TimeoutError,
|
||||
NotConnectedError,
|
||||
AccountLinesRequest,
|
||||
IssuedCurrency,
|
||||
} from '../../src'
|
||||
import { Payment, Transaction } from '../../src/models/transactions'
|
||||
import { hashSignedTx } from '../../src/utils/hashes'
|
||||
|
||||
const masterAccount = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
const masterSecret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
|
||||
export const GENESIS_ACCOUNT = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
|
||||
const GENESIS_SECRET = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb'
|
||||
|
||||
export async function sendLedgerAccept(client: Client): Promise<unknown> {
|
||||
return client.connection.request({ command: 'ledger_accept' })
|
||||
@@ -141,12 +143,12 @@ export async function fundAccount(
|
||||
): Promise<SubmitResponse> {
|
||||
const payment: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: masterAccount,
|
||||
Account: GENESIS_ACCOUNT,
|
||||
Destination: wallet.classicAddress,
|
||||
// 2 times the amount needed for a new account (20 XRP)
|
||||
Amount: '400000000',
|
||||
}
|
||||
const wal = Wallet.fromSeed(masterSecret)
|
||||
const wal = Wallet.fromSeed(GENESIS_SECRET)
|
||||
const response = await submitTransaction({
|
||||
client,
|
||||
wallet: wal,
|
||||
@@ -263,11 +265,13 @@ export async function testTransaction(
|
||||
|
||||
export async function getXRPBalance(
|
||||
client: Client,
|
||||
wallet: Wallet,
|
||||
account: string | Wallet,
|
||||
): Promise<string> {
|
||||
const address: string =
|
||||
typeof account === 'string' ? account : account.classicAddress
|
||||
const request: AccountInfoRequest = {
|
||||
command: 'account_info',
|
||||
account: wallet.classicAddress,
|
||||
account: address,
|
||||
}
|
||||
return (await client.request(request)).result.account_data.Balance
|
||||
}
|
||||
@@ -338,3 +342,16 @@ export async function waitForAndForceProgressLedgerTime(
|
||||
|
||||
throw new Error(`Ledger time not reached after ${retries} retries.`)
|
||||
}
|
||||
|
||||
export async function getIOUBalance(
|
||||
client: Client,
|
||||
wallet: Wallet,
|
||||
currency: IssuedCurrency,
|
||||
): Promise<string> {
|
||||
const request: AccountLinesRequest = {
|
||||
command: 'account_lines',
|
||||
account: wallet.classicAddress,
|
||||
peer: currency.issuer,
|
||||
}
|
||||
return (await client.request(request)).result.lines[0].balance
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user