From 3532c4575a01bba4494d647e9d58884a7feb7de8 Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Tue, 6 Feb 2024 14:46:27 +0100 Subject: [PATCH 1/4] add firewall hook --- contracts/firewall/firewall_base.c | 107 +++ contracts/firewall/firewall_provider.c | 97 +++ .../integration/firewall/firewallBase.test.ts | 623 ++++++++++++++++++ .../firewall/firewallProvider.test.ts | 273 ++++++++ 4 files changed, 1100 insertions(+) create mode 100644 contracts/firewall/firewall_base.c create mode 100644 contracts/firewall/firewall_provider.c create mode 100644 test/integration/firewall/firewallBase.test.ts create mode 100644 test/integration/firewall/firewallProvider.test.ts diff --git a/contracts/firewall/firewall_base.c b/contracts/firewall/firewall_base.c new file mode 100644 index 0000000..784b589 --- /dev/null +++ b/contracts/firewall/firewall_base.c @@ -0,0 +1,107 @@ +#include "hookapi.h" +#include +/** + This hook is an omnibus hook that contains 2 different hooks' functionalities. Each of these + can be enabled or disabled and configured using the provided install-time hook parameter as + described below: + + All integer values are little endian unless otherwise marked + + Firewall Hook + Parameter Name: 0x4650 ('FP') + Parameter Value: <20 byte account ID of blocklist provider> + Parameter Name: 0x4649 ('FI') + Parameter Value: + Parameter Name: 0x464F ('FO') + Parameter Value: + Parameter Name: 0x4644 ('FD') + Parameter Value: minimum drops threshold for incoming XRP payments (xfl LE) + Parameter Name: 0x4654 ('FT') + Parameter Value: minimum threshold for incoming trustline payments (xfl LE) + +**/ + +uint8_t tts[32] = { + 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, + 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU +}; + +int64_t hook(uint32_t r) +{ + _g(1,1); + + // get the account id + uint8_t otxn_account[20]; + otxn_field(SBUF(otxn_account), sfAccount); + + uint8_t hook_acc[20]; + hook_account(SBUF(hook_acc)); + + uint8_t outgoing = BUFFER_EQUAL_20(hook_acc, otxn_account); + + uint32_t tt = otxn_type(); + + if (tt == 22) + accept(SBUF("Firewall: Passing SetHook txn"), __LINE__); + + // get the relevant amount, if any + int64_t amount = -1; + int64_t amount_native; + otxn_slot(1); + + if (slot_subfield(1, sfAmount, 1) == 1) + { + amount = slot_float(1); + amount_native = slot_size(1) == 9; + } + + // check flags + uint8_t flagbuf[4]; + otxn_field(SBUF(flagbuf), sfFlags); + + // Blocklist + { + uint8_t param_name[2] = {'F', 'P'}; + uint8_t provider[20]; + hook_param(SBUF(provider), SBUF(param_name)); + uint8_t dummy[64]; + if (state_foreign(dummy, 32, SBUF(otxn_account), dummy + 32, 32, SBUF(provider)) > 0) + rollback(SBUF("Blocklist match"), __LINE__); + } + + // Firewall + { + // check allowable txn types + { + uint8_t param_name[2] = {'F', outgoing ? 'O' : 'I'}; + + hook_param(tts, 32, SBUF(param_name)); + + // check if its on the list of blocked txn types + if (tts[tt >> 3] & (tt % 8)) + rollback(SBUF("Firewall blocked txn type"), __LINE__); + + } + + // if its an incoming payment ensure it passes the threshold + if (!outgoing && amount >= 0) + { + + if (flagbuf[2] & 2U) + rollback(SBUF("Firewall blocked incoming partial payment"), __LINE__); + + // threshold for drops + uint8_t param_name[2] = {'F', amount_native ? 'D' : 'T'}; + + // if the parameter doesn't exist then the threshold is unlimited or rather 9.999999999999999e+95 + int64_t threshold = 7810234554605699071LL; + hook_param(&threshold, 8, SBUF(param_name)); + if (float_compare(amount, threshold, COMPARE_LESS) == 1) + rollback(SBUF("Firewall blocked amount below threshold"), __LINE__); + + } + + } + + return accept(SBUF("Firewall: Passing txn within thresholds"), __LINE__); +} \ No newline at end of file diff --git a/contracts/firewall/firewall_provider.c b/contracts/firewall/firewall_provider.c new file mode 100644 index 0000000..bf38bfa --- /dev/null +++ b/contracts/firewall/firewall_provider.c @@ -0,0 +1,97 @@ +#include "hookapi.h" +#include + +#define ASSERT(x)\ +{\ + if (!(x))\ + rollback(0,0,__LINE__);\ +} + +#define DONE()\ + accept(0,0,__LINE__) + +/** + * Account Owner can: + * ttINVOKE: + * Blob: (packed data) (up to 32) + * ( 1 byte action type + 20 byte account id ) + + * + * Action Type: 0 to remove account, 1 to add account + */ +int64_t hook(uint32_t r) +{ + _g(1,1); + + uint8_t ttbuf[16]; + int64_t br = otxn_field(SBUF(ttbuf), sfTransactionType); + uint32_t txntype = ((uint32_t)(ttbuf[0]) << 16U) + ((uint32_t)(ttbuf[1])); + + // pass anything that isn't a ttINVOKE + if (txntype != 99) + DONE(); + + // get the account id + uint8_t account_field[20]; + ASSERT(otxn_field(SBUF(account_field), sfAccount) == 20); + + uint8_t hook_accid[20]; + hook_account(SBUF(hook_accid)); + + // ignore anything that isn't a self invoke + if (!BUFFER_EQUAL_20(hook_accid, account_field)) + DONE(); + + // if there's a destination set + uint8_t dest[20]; + if (otxn_field(SBUF(dest), sfDestination) == 20) + { + if (!BUFFER_EQUAL_20(hook_accid, dest)) + { + // .. then they are invoking someone else's hook + // and we need to not interfere with that. + DONE(); + } + } + + uint8_t txn_id[32]; + ASSERT(otxn_id(txn_id, 32, 0) == 32); + +#define sfBlob ((7U << 16U) + 26U) + + ASSERT(otxn_slot(1) == 1); + ASSERT(slot_subfield(1, sfBlob, 2) == 2); + + uint8_t buffer[676]; + + ASSERT(slot(SBUF(buffer), 2) > 0); + + trace(SBUF("blob-buffer"), buffer, 676, 1); + + uint16_t len = (uint16_t)buffer[0]; + uint8_t* ptr = buffer + 1; + if (len > 192) + { + len = 193 + ((len - 193) * 256) + ((uint16_t)(buffer[1])); + ptr++; + } + + uint8_t* end = ptr + len; + + // execution to here means it's a valid modification instruction + while (ptr < end) + { + GUARD(32); + + uint8_t* dptr = *ptr == 0 ? txn_id : 0; + uint64_t dlen = *ptr == 0 ? 32 : 0; + ASSERT((state_set( + dptr, dlen, + ptr+1, 20 + ) == dlen); + + ptr += 21; + } + + DONE(); + +} \ No newline at end of file diff --git a/test/integration/firewall/firewallBase.test.ts b/test/integration/firewall/firewallBase.test.ts new file mode 100644 index 0000000..de06cf1 --- /dev/null +++ b/test/integration/firewall/firewallBase.test.ts @@ -0,0 +1,623 @@ +import { + Invoke, + // AccountSet, + Payment, + // SetHook, + SetHookFlags, + TransactionMetadata, + calculateHookOn, + // xrpToDrops, +} from '@transia/xrpl' +import { IssuedCurrencyAmount } from '@transia/xrpl/dist/npm/models/common' +import { AccountID, Currency } from '@transia/ripple-binary-codec/dist/types' +import { + XrplIntegrationTestContext, + serverUrl, + setupClient, + teardownClient, +} from '@transia/hooks-toolkit/dist/npm/src/libs/xrpl-helpers' +import { + Xrpld, + ExecutionUtility, + createHookPayload, + // floatToLEXfl, + setHooksV3, + SetHookParams, + iHookParamEntry, + iHookParamName, + iHookParamValue, + formatAccountCurrencyBlob, + formatAccountBlob, + floatToLEXfl, +} from '@transia/hooks-toolkit' + +// Firewall.Base: ACCEPT: passing sethook tt +// Firewall.Base: ACCEPT: amount < 0 +// Firewall.Base: ROLLBACK: block account - TODO +// Firewall.Base: ROLLBACK: block in tt +// Firewall.Base: ROLLBACK: block out tt +// Firewall.Base: ROLLBACK: block min xrp +// Firewall.Base: ROLLBACK: block max xrp +// Firewall.Base: ROLLBACK: block min usd +// Firewall.Base: ROLLBACK: block max xrp +// Firewall.Base: ACCEPT: Passthrough + +describe('firewall.base - Success Group', () => { + let testContext: XrplIntegrationTestContext + + beforeAll(async () => { + testContext = await setupClient(serverUrl) + }) + afterAll(async () => teardownClient(testContext)) + + it('firewall base - ic whitelist', async () => { + const aliceWallet = testContext.alice + const bobWallet = testContext.bob + const elsaWallet = testContext.elsa + const elsaHex = AccountID.from(elsaWallet.classicAddress).toHex() + const currencyHex = Currency.from('USD').toHex() + const hook1Param1 = new iHookParamEntry( + new iHookParamName('FWP'), + new iHookParamValue(elsaHex, true) + ) + const hook1Param2 = new iHookParamEntry( + new iHookParamName('FIC'), + new iHookParamValue(currencyHex, true) + ) + const hook1Param3 = new iHookParamEntry( + new iHookParamName('FIP'), + new iHookParamValue(elsaHex, true) + ) + const hook1Param4 = new iHookParamEntry( + new iHookParamName('FI'), + new iHookParamValue(calculateHookOn(['Payment']), true) + ) + const hook1Param5 = new iHookParamEntry( + new iHookParamName('FLD'), + new iHookParamValue(floatToLEXfl('1'), true) + ) + const hook1Param6 = new iHookParamEntry( + new iHookParamName('FUD'), + new iHookParamValue(floatToLEXfl('100'), true) + ) + const hook1 = createHookPayload({ + version: 0, + createFile: 'firewall_base', + namespace: 'firewall_base', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Payment'], + hookParams: [ + hook1Param1.toXrpl(), + hook1Param2.toXrpl(), + hook1Param3.toXrpl(), + hook1Param4.toXrpl(), + hook1Param5.toXrpl(), + hook1Param6.toXrpl(), + ], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook1 }], + } as SetHookParams) + const hook2 = createHookPayload({ + version: 0, + createFile: 'firewall_provider', + namespace: 'firewall_provider', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Invoke'], + }) + hook2.HookNamespace = + '0000000000000000000000000000000000000000000000000000000000000000' + await setHooksV3({ + client: testContext.client, + seed: testContext.elsa.seed, + hooks: [{ Hook: hook2 }], + } as SetHookParams) + + // INVOKE OUT + const tx1Param1 = new iHookParamEntry( + new iHookParamName('M'), + new iHookParamValue(floatToLEXfl('1'), true) + ) + const tx1blob = formatAccountBlob([bobWallet.classicAddress]) + const builtTx1: Invoke = { + TransactionType: 'Invoke', + Account: elsaWallet.classicAddress, + Blob: tx1blob, + HookParameters: [tx1Param1.toXrpl()], + } + const result1 = await Xrpld.submit(testContext.client, { + wallet: elsaWallet, + tx: builtTx1, + }) + + const hook1Emitted = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result1.meta as TransactionMetadata + ) + expect(hook1Emitted.executions[0].HookReturnString).toMatch('') + + // INVOKE OUT + const tx2Param1 = new iHookParamEntry( + new iHookParamName('M'), + new iHookParamValue(floatToLEXfl('2'), true) + ) + const tx2blob = formatAccountCurrencyBlob('USD', [ + testContext.gw.classicAddress, + ]) + const builtTx2: Invoke = { + TransactionType: 'Invoke', + Account: elsaWallet.classicAddress, + Blob: tx2blob, + HookParameters: [tx2Param1.toXrpl()], + } + + const result2 = await Xrpld.submit(testContext.client, { + wallet: elsaWallet, + tx: builtTx2, + }) + + const hook2Emitted = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result2.meta as TransactionMetadata + ) + expect(hook2Emitted.executions[0].HookReturnString).toMatch('') + + // PAYMENT IN + const amount: IssuedCurrencyAmount = { + value: '100', + currency: 'USD', + issuer: testContext.gw.classicAddress, + } + const builtTx: Payment = { + TransactionType: 'Payment', + Account: bobWallet.classicAddress, + Destination: aliceWallet.classicAddress, + Amount: amount, + } + + const result = await Xrpld.submit(testContext.client, { + wallet: bobWallet, + tx: builtTx, + }) + + const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata + ) + expect(hookEmitted.executions[0].HookReturnString).toMatch( + 'Firewall: Passing SetHook txn' + ) + }) + + // it('firewall base - passing sethook tt', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook']), true) + // ) + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['SetHook'], + // [param1.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // const aliceWallet = testContext.alice + // const builtTx: SetHook = { + // TransactionType: 'SetHook', + // Account: aliceWallet.address, + // Hooks: [ + // { + // Hook: createHookPayload(0, 'base', 'base', SetHookFlags.hsfOverride, [ + // 'Payment', + // ]), + // }, + // ], + // } + + // const result = await Xrpld.submit(testContext.client, { + // wallet: aliceWallet, + // tx: builtTx, + // }) + + // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + // testContext.client, + // result.meta as TransactionMetadata + // ) + // expect(hookEmitted.executions[0].HookReturnString).toMatch( + // 'Firewall: Passing SetHook txn' + // ) + // }) + + // it('firewall base - amount < 0', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook', 'AccountSet']), true) + // ) + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['AccountSet'], + // [param1.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // // ACCOUNT SET OUT + // const aliceWallet = testContext.alice + // const builtTx: AccountSet = { + // TransactionType: 'AccountSet', + // Account: aliceWallet.address, + // } + // const result = await Xrpld.submit(testContext.client, { + // wallet: aliceWallet, + // tx: builtTx, + // }) + + // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + // testContext.client, + // result.meta as TransactionMetadata + // ) + // expect(hookEmitted.executions[0].HookReturnString).toMatch( + // 'Firewall: Ignoring negative amount' + // ) + // }) + + // it('firewall base - block in tt', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook']), true) + // ) + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // try { + // // PAYMENT IN + // const aliceWallet = testContext.alice + // const bobWallet = testContext.bob + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: bobWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // Amount: xrpToDrops(100), + // } + // await Xrpld.submit(testContext.client, { + // wallet: bobWallet, + // tx: builtTx, + // }) + // } catch (error: unknown) { + // if (error instanceof Error) { + // expect(error.message).toMatch('Firewall: blocking txn type') + // } + // } + // }) + + // it('firewall base - block out tt', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FO'), + // new iHookParamValue(calculateHookOn(['SetHook']), true) + // ) + + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // try { + // // PAYMENT OUT + // const aliceWallet = testContext.alice + // const carolWallet = testContext.carol + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: aliceWallet.classicAddress, + // Destination: carolWallet.classicAddress, + // Amount: xrpToDrops(100), + // } + // await Xrpld.submit(testContext.client, { + // wallet: aliceWallet, + // tx: builtTx, + // }) + // } catch (error: unknown) { + // if (error instanceof Error) { + // expect(error.message).toMatch('Firewall: blocking txn type') + // } + // } + // }) + + // it('firewall base - block min xrp', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + // ) + // const param2 = new iHookParamEntry( + // new iHookParamName('FLD'), + // new iHookParamValue(floatToLEXfl('100'), true) + // ) + + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl(), param2.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // try { + // // PAYMENT IN + // const aliceWallet = testContext.alice + // const carolWallet = testContext.carol + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: carolWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // Amount: xrpToDrops(99), + // } + // await Xrpld.submit(testContext.client, { + // wallet: carolWallet, + // tx: builtTx, + // }) + // throw Error('Expected Failure') + // } catch (error: unknown) { + // if (error instanceof Error) { + // console.log(error.message) + // expect(error.message).toMatch( + // 'Firewall: blocking amount below threshold' + // ) + // } + // } + // }) + + // it('firewall base - block max xrp', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + // ) + // const param2 = new iHookParamEntry( + // new iHookParamName('FLD'), + // new iHookParamValue(floatToLEXfl('10'), true) + // ) + // const param3 = new iHookParamEntry( + // new iHookParamName('FUD'), + // new iHookParamValue(floatToLEXfl('100'), true) + // ) + + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl(), param2.toXrpl(), param3.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // try { + // // PAYMENT IN + // const aliceWallet = testContext.alice + // const carolWallet = testContext.carol + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: carolWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // Amount: xrpToDrops(101), + // } + // await Xrpld.submit(testContext.client, { + // wallet: carolWallet, + // tx: builtTx, + // }) + // throw Error('Expected Failure') + // } catch (error: unknown) { + // if (error instanceof Error) { + // expect(error.message).toMatch( + // 'Firewall: blocking amount above threshold' + // ) + // } + // } + // }) + + // it('firewall base - block min amount', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + // ) + // const param2 = new iHookParamEntry( + // new iHookParamName('FLT'), + // new iHookParamValue(floatToLEXfl('100'), true) + // ) + + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl(), param2.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // const amount: IssuedCurrencyAmount = { + // value: '99', + // currency: 'USD', + // issuer: testContext.gw.classicAddress, + // } + + // try { + // // PAYMENT IN + // const aliceWallet = testContext.alice + // const carolWallet = testContext.carol + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: carolWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // Amount: amount, + // } + // await Xrpld.submit(testContext.client, { + // wallet: carolWallet, + // tx: builtTx, + // }) + // } catch (error: unknown) { + // if (error instanceof Error) { + // expect(error.message).toMatch( + // 'Firewall: blocking amount below threshold' + // ) + // } + // } + // }) + // it('firewall base - block max amount', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + // ) + // const param2 = new iHookParamEntry( + // new iHookParamName('FLT'), + // new iHookParamValue(floatToLEXfl('10'), true) + // ) + // const param3 = new iHookParamEntry( + // new iHookParamName('FUT'), + // new iHookParamValue(floatToLEXfl('100'), true) + // ) + + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl(), param2.toXrpl(), param3.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // const amount: IssuedCurrencyAmount = { + // value: '101', + // currency: 'USD', + // issuer: testContext.gw.classicAddress, + // } + // try { + // // PAYMENT IN + // const aliceWallet = testContext.alice + // const carolWallet = testContext.carol + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: carolWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // Amount: amount, + // } + // await Xrpld.submit(testContext.client, { + // wallet: carolWallet, + // tx: builtTx, + // }) + // } catch (error: unknown) { + // if (error instanceof Error) { + // expect(error.message).toMatch( + // 'Firewall: blocking amount above threshold' + // ) + // } + // } + // }) + + // it('firewall - passthrough', async () => { + // const param1 = new iHookParamEntry( + // new iHookParamName('FI'), + // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + // ) + // const param2 = new iHookParamEntry( + // new iHookParamName('FO'), + // new iHookParamValue(calculateHookOn(['SetHook']), true) + // ) + // const param3 = new iHookParamEntry( + // new iHookParamName('FLD'), + // new iHookParamValue(floatToLEXfl('1'), true) + // ) + // const param4 = new iHookParamEntry( + // new iHookParamName('FUD'), + // new iHookParamValue(floatToLEXfl('100'), true) + // ) + + // const hook = createHookPayload( + // 0, + // 'firewall_base', + // 'firewall_base', + // SetHookFlags.hsfOverride, + // ['Payment'], + // [param1.toXrpl(), param2.toXrpl(), param3.toXrpl(), param4.toXrpl()] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // // PAYMENT IN + // const aliceWallet = testContext.alice + // const carolWallet = testContext.carol + // const builtTx: Payment = { + // TransactionType: 'Payment', + // Account: carolWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // Amount: xrpToDrops(100), + // } + // const result = await Xrpld.submit(testContext.client, { + // wallet: carolWallet, + // tx: builtTx, + // }) + + // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + // testContext.client, + // result.meta as TransactionMetadata + // ) + // expect(hookEmitted.executions[0].HookReturnString).toMatch( + // 'Firewall: Passing txn within thresholds' + // ) + // }) +}) diff --git a/test/integration/firewall/firewallProvider.test.ts b/test/integration/firewall/firewallProvider.test.ts new file mode 100644 index 0000000..d024be9 --- /dev/null +++ b/test/integration/firewall/firewallProvider.test.ts @@ -0,0 +1,273 @@ +import { + Invoke, + LedgerEntryRequest, + // Payment, + SetHookFlags, + TransactionMetadata, + // xrpToDrops, +} from '@transia/xrpl' +import { AccountID } from '@transia/ripple-binary-codec/dist/types' +import { + HookDefinition as LeHookDefinition, + Hook as LeHook, +} from '@transia/xrpl/dist/npm/models/ledger' +import { + XrplIntegrationTestContext, + setupClient, + teardownClient, + serverUrl, +} from '@transia/hooks-toolkit/dist/npm/src/libs/xrpl-helpers' +import { + Xrpld, + ExecutionUtility, + createHookPayload, + setHooksV3, + SetHookParams, + formatAccountBlob, + StateUtility, + padHexString, + // padHexString, +} from '@transia/hooks-toolkit' + +// Firewall.Provider: ACCEPT: TT != Invoke +// Firewall.Provider: ACCEPT: TX ACCOUNT != HOOK +// Firewall.Provider: ACCEPT: TX Destination != HOOK + +// Firewall.Provider: ACCEPT: Add Account +// Firewall.Provider: ACCEPT: Remove Account +// Firewall.Provider: ACCEPT: Add Mutliple - TODO +// Firewall.Provider: ACCEPT: Remove Mutliple - TODO + +// Firewall.Provider: ACCEPT: Test Max - TODO +// Firewall.Provider: ACCEPT: Test Fee for exponential growth - TODO + +describe('Application.firewall_provider - Success Group', () => { + let testContext: XrplIntegrationTestContext + + beforeAll(async () => { + testContext = await setupClient(serverUrl) + }) + afterAll(async () => teardownClient(testContext)) + + it('firewall base - tt != invoke', async () => { + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_provider', + namespace: 'firewall_provider', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Invoke'], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + + // PAYMENT IN + const aliceWallet = testContext.alice + const bobWallet = testContext.bob + const builtTx: Payment = { + TransactionType: 'Payment', + Account: bobWallet.classicAddress, + Destination: aliceWallet.classicAddress, + Amount: xrpToDrops(100), + } + const result = await Xrpld.submit(testContext.client, { + wallet: bobWallet, + tx: builtTx, + }) + + const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata + ) + expect(hookEmitted.executions[0].HookReturnString).toMatch('') + }) + + // INVALID FAIL + // it('firewall base - tx account != hook', async () => { + // const hook = createHookPayload( + // 0, + // 'firewall_provider', + // 'firewall_provider', + // SetHookFlags.hsfOverride, + // ['Invoke'] + // ) + // await setHooksV3({ + // client: testContext.client, + // seed: testContext.alice.seed, + // hooks: [{ Hook: hook }], + // } as SetHookParams) + + // // INVOKE IN + // const aliceWallet = testContext.alice + // const bobWallet = testContext.bob + // const builtTx: Invoke = { + // TransactionType: 'Invoke', + // Account: bobWallet.classicAddress, + // Destination: aliceWallet.classicAddress, + // } + // const result = await Xrpld.submit(testContext.client, { + // wallet: aliceWallet, + // tx: builtTx, + // }) + + // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + // testContext.client, + // result.meta as TransactionMetadata + // ) + // expect(hookEmitted.executions[0].HookReturnString).toMatch('') + // }) + + it('firewall base - tx dest != hook', async () => { + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_provider', + namespace: 'firewall_provider', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Invoke'], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + + // INVOKE OUT + const aliceWallet = testContext.alice + const bobWallet = testContext.bob + const builtTx: Invoke = { + TransactionType: 'Invoke', + Account: bobWallet.classicAddress, + Destination: aliceWallet.classicAddress, + } + const result = await Xrpld.submit(testContext.client, { + wallet: bobWallet, + tx: builtTx, + }) + const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata + ) + expect(hookEmitted.executions[0].HookReturnString).toMatch('') + }) + + it('firewall provider - add account state', async () => { + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_provider', + namespace: 'firewall_provider', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Invoke'], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + + // INVOKE OUT + const aliceWallet = testContext.alice + const blob = formatAccountBlob([testContext.carol.classicAddress]) + const builtTx: Invoke = { + TransactionType: 'Invoke', + Account: aliceWallet.classicAddress, + Blob: blob, + } + const result = await Xrpld.submit(testContext.client, { + wallet: aliceWallet, + tx: builtTx, + }) + + const leHook = await StateUtility.getHook( + testContext.client, + testContext.alice.classicAddress + ) + const hookDefRequest: LedgerEntryRequest = { + command: 'ledger_entry', + hook_definition: leHook.Hooks[0].Hook.HookHash, + } + const hookDefRes = await testContext.client.request(hookDefRequest) + const leHookDef = hookDefRes.result.node as LeHookDefinition + + const aliceAccHex = AccountID.from(aliceWallet.classicAddress).toHex() + const hookState = await StateUtility.getHookState( + testContext.client, + testContext.alice.classicAddress, + padHexString(aliceAccHex), + leHookDef.HookNamespace as string + ) + const hookExecutions = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata + ) + // DONEEMPTY + expect(hookExecutions.executions[0].HookReturnString).toEqual('') + expect(hookState.HookStateData).toBeDefined() + }) + + it('firewall provider - remove account state', async () => { + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_provider', + namespace: 'firewall_provider', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Invoke'], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + + // INVOKE OUT + const aliceWallet = testContext.alice + const blob = formatAccountBlob([testContext.carol.classicAddress]) + const builtTx: Invoke = { + TransactionType: 'Invoke', + Account: aliceWallet.classicAddress, + Blob: blob, + } + const result = await Xrpld.submit(testContext.client, { + wallet: aliceWallet, + tx: builtTx, + }) + + const hookExecutions = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata + ) + // DONEEMPTY + expect(hookExecutions.executions[0].HookReturnString).toEqual('') + + try { + const hookReq: LedgerEntryRequest = { + command: 'ledger_entry', + hook: { + account: testContext.alice.classicAddress, + }, + } + const hookRes = await testContext.client.request(hookReq) + const leHook = hookRes.result.node as LeHook + const hookDefRequest: LedgerEntryRequest = { + command: 'ledger_entry', + hook_definition: leHook.Hooks[0].Hook.HookHash, + } + const hookDefRes = await testContext.client.request(hookDefRequest) + const leHookDef = hookDefRes.result.node as LeHookDefinition + + const aliceAccHex = AccountID.from(aliceWallet.classicAddress).toHex() + const hookState = await StateUtility.getHookState( + testContext.client, + testContext.alice.classicAddress, + padHexString(aliceAccHex), + leHookDef.HookNamespace as string + ) + expect(hookState.HookStateData).toBeDefined() + } catch (error: unknown) { + if (error instanceof Error) { + expect(error.message).toEqual('entryNotFound') + } + } + }) +}) From e6f3298c50d9b06d1532f2fc1cf2b82f75855d6a Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Tue, 13 Feb 2024 16:01:55 +0100 Subject: [PATCH 2/4] add tts.h --- contracts/utils/hookapi.h | 1 + contracts/utils/macro.h | 37 ---------------------------------- contracts/utils/tts.h | 42 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 37 deletions(-) create mode 100644 contracts/utils/tts.h diff --git a/contracts/utils/hookapi.h b/contracts/utils/hookapi.h index a97a488..9627f8a 100644 --- a/contracts/utils/hookapi.h +++ b/contracts/utils/hookapi.h @@ -53,5 +53,6 @@ #include "sfcodes.h" #include "macro.h" #include "date.h" +#include "tts.h" #endif diff --git a/contracts/utils/macro.h b/contracts/utils/macro.h index 7039092..f25c21f 100644 --- a/contracts/utils/macro.h +++ b/contracts/utils/macro.h @@ -301,43 +301,6 @@ int out_len = 0;\ if (i < 0) buf[0] |= 0x80U;\ } -#define ttPAYMENT 0 -#define ttESCROW_CREATE 1 -#define ttESCROW_FINISH 2 -#define ttACCOUNT_SET 3 -#define ttESCROW_CANCEL 4 -#define ttREGULAR_KEY_SET 5 -#define ttOFFER_CREATE 7 -#define ttOFFER_CANCEL 8 -#define ttTICKET_CREATE 10 -#define ttSIGNER_LIST_SET 12 -#define ttPAYCHAN_CREATE 13 -#define ttPAYCHAN_FUND 14 -#define ttPAYCHAN_CLAIM 15 -#define ttCHECK_CREATE 16 -#define ttCHECK_CASH 17 -#define ttCHECK_CANCEL 18 -#define ttDEPOSIT_PREAUTH 19 -#define ttTRUST_SET 20 -#define ttACCOUNT_DELETE 21 -#define ttSET_HOOK 22 -#define ttNFTOKEN_MINT 25 -#define ttNFTOKEN_BURN 26 -#define ttNFTOKEN_CREATE_OFFER 27 -#define ttNFTOKEN_CANCEL_OFFER 28 -#define ttNFTOKEN_ACCEPT_OFFER 29 -#define ttURITOKEN_MINT 45 -#define ttURITOKEN_BURN 46 -#define ttURITOKEN_BUY 47 -#define ttURITOKEN_CREATE_SELL_OFFER 48 -#define ttURITOKEN_CANCEL_SELL_OFFER 49 -#define ttIMPORT 97 -#define ttCLAIM_REWARD 98 -#define ttINVOKE 99 -#define ttAMENDMENT 100 -#define ttFEE 101 -#define ttUNL_MODIFY 102 -#define ttEMIT_FAILURE 103 #define tfCANONICAL 0x80000000UL #define atACCOUNT 1U diff --git a/contracts/utils/tts.h b/contracts/utils/tts.h new file mode 100644 index 0000000..304685c --- /dev/null +++ b/contracts/utils/tts.h @@ -0,0 +1,42 @@ +// For documentation please see: https://xrpl-hooks.readme.io/reference/ +#define ttPAYMENT 0 +#define ttESCROW_CREATE 1 +#define ttESCROW_FINISH 2 +#define ttACCOUNT_SET 3 +#define ttESCROW_CANCEL 4 +#define ttREGULAR_KEY_SET 5 +// #define ttNICKNAME_SET 6 // deprecated +#define ttOFFER_CREATE 7 +#define ttOFFER_CANCEL 8 +#define ttTICKET_CREATE 10 +// #define ttSPINAL_TAP 11 // deprecated +#define ttSIGNER_LIST_SET 12 +#define ttPAYCHAN_CREATE 13 +#define ttPAYCHAN_FUND 14 +#define ttPAYCHAN_CLAIM 15 +#define ttCHECK_CREATE 16 +#define ttCHECK_CASH 17 +#define ttCHECK_CANCEL 18 +#define ttDEPOSIT_PREAUTH 19 +#define ttTRUST_SET 20 +#define ttACCOUNT_DELETE 21 +#define ttHOOK_SET 22 +#define ttNFTOKEN_MINT 25 +#define ttNFTOKEN_BURN 26 +#define ttNFTOKEN_CREATE_OFFER 27 +#define ttNFTOKEN_CANCEL_OFFER 28 +#define ttNFTOKEN_ACCEPT_OFFER 29 +#define ttURITOKEN_MINT 45 +#define ttURITOKEN_BURN 46 +#define ttURITOKEN_BUY 47 +#define ttURITOKEN_CREATE_SELL_OFFER 48 +#define ttURITOKEN_CANCEL_SELL_OFFER 49 +#define ttGENESIS_MINT 96 +#define ttIMPORT 97 +#define ttCLAIM_REWARD 98 +#define ttINVOKE 99 +#define ttAMENDMENT 100 +#define ttFEE 101 +#define ttUNL_MODIFY 102 +#define ttEMIT_FAILURE 103 +#define ttUNL_REPORT 104 \ No newline at end of file From f9999a48885d145d74a5c763dc41d98bad9b0050 Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Tue, 13 Feb 2024 16:02:00 +0100 Subject: [PATCH 3/4] Update README.md --- test/integration/router/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/router/README.md b/test/integration/router/README.md index 16e93b1..90846c6 100644 --- a/test/integration/router/README.md +++ b/test/integration/router/README.md @@ -51,7 +51,7 @@ The router hook logic is implemented in `router_base.c`. It reads the hook posit To debug the router hook, you can monitor the `xrpld` logs for `HookTrace` entries: ```shell -tail -f xrpld/log/debug.log | grep HookTrace +tail -f xahau/log/debug.log | grep HookTrace ``` ## Contributing From f4e0b7ac01635b7a1c85502f7c1a69de887769db Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Tue, 13 Feb 2024 16:02:10 +0100 Subject: [PATCH 4/4] update firewall hooks and tests --- contracts/firewall/base.c | 14 + contracts/firewall/firewall_base.c | 20 +- contracts/firewall/firewall_provider.c | 12 +- .../integration/firewall/firewallBase.test.ts | 793 ++++++------------ .../firewall/firewallProvider.test.ts | 125 +-- 5 files changed, 303 insertions(+), 661 deletions(-) create mode 100644 contracts/firewall/base.c diff --git a/contracts/firewall/base.c b/contracts/firewall/base.c new file mode 100644 index 0000000..8c1f174 --- /dev/null +++ b/contracts/firewall/base.c @@ -0,0 +1,14 @@ +/** + * + */ +#include "hookapi.h" + +int64_t hook(uint32_t reserved) { + + TRACESTR("Base.c: Called."); + accept(SBUF("base: Finished."), __LINE__); + + _g(1,1); + // unreachable + return 0; +} \ No newline at end of file diff --git a/contracts/firewall/firewall_base.c b/contracts/firewall/firewall_base.c index 784b589..343f229 100644 --- a/contracts/firewall/firewall_base.c +++ b/contracts/firewall/firewall_base.c @@ -42,7 +42,7 @@ int64_t hook(uint32_t r) uint32_t tt = otxn_type(); if (tt == 22) - accept(SBUF("Firewall: Passing SetHook txn"), __LINE__); + accept(SBUF("Firewall: Ignoring SetHook txn"), __LINE__); // get the relevant amount, if any int64_t amount = -1; @@ -52,7 +52,7 @@ int64_t hook(uint32_t r) if (slot_subfield(1, sfAmount, 1) == 1) { amount = slot_float(1); - amount_native = slot_size(1) == 9; + amount_native = slot_size(1) == 8; } // check flags @@ -66,7 +66,7 @@ int64_t hook(uint32_t r) hook_param(SBUF(provider), SBUF(param_name)); uint8_t dummy[64]; if (state_foreign(dummy, 32, SBUF(otxn_account), dummy + 32, 32, SBUF(provider)) > 0) - rollback(SBUF("Blocklist match"), __LINE__); + rollback(SBUF("Firewall: Blocklist match"), __LINE__); } // Firewall @@ -79,7 +79,7 @@ int64_t hook(uint32_t r) // check if its on the list of blocked txn types if (tts[tt >> 3] & (tt % 8)) - rollback(SBUF("Firewall blocked txn type"), __LINE__); + rollback(SBUF("Firewall: Txn type"), __LINE__); } @@ -88,20 +88,22 @@ int64_t hook(uint32_t r) { if (flagbuf[2] & 2U) - rollback(SBUF("Firewall blocked incoming partial payment"), __LINE__); + rollback(SBUF("Firewall: Incoming partial payment"), __LINE__); + TRACEVAR(amount_native); // threshold for drops uint8_t param_name[2] = {'F', amount_native ? 'D' : 'T'}; + TRACEHEX(param_name); // if the parameter doesn't exist then the threshold is unlimited or rather 9.999999999999999e+95 int64_t threshold = 7810234554605699071LL; hook_param(&threshold, 8, SBUF(param_name)); + TRACEVAR(amount); + TRACEVAR(threshold); if (float_compare(amount, threshold, COMPARE_LESS) == 1) - rollback(SBUF("Firewall blocked amount below threshold"), __LINE__); - + rollback(SBUF("Firewall: Amount below threshold"), __LINE__); } - } - return accept(SBUF("Firewall: Passing txn within thresholds"), __LINE__); + return accept(SBUF("Firewall: Txn within thresholds"), __LINE__); } \ No newline at end of file diff --git a/contracts/firewall/firewall_provider.c b/contracts/firewall/firewall_provider.c index bf38bfa..91139d0 100644 --- a/contracts/firewall/firewall_provider.c +++ b/contracts/firewall/firewall_provider.c @@ -22,12 +22,8 @@ int64_t hook(uint32_t r) { _g(1,1); - uint8_t ttbuf[16]; - int64_t br = otxn_field(SBUF(ttbuf), sfTransactionType); - uint32_t txntype = ((uint32_t)(ttbuf[0]) << 16U) + ((uint32_t)(ttbuf[1])); - // pass anything that isn't a ttINVOKE - if (txntype != 99) + if (otxn_type() != ttINVOKE) DONE(); // get the account id @@ -84,11 +80,7 @@ int64_t hook(uint32_t r) uint8_t* dptr = *ptr == 0 ? txn_id : 0; uint64_t dlen = *ptr == 0 ? 32 : 0; - ASSERT((state_set( - dptr, dlen, - ptr+1, 20 - ) == dlen); - + ASSERT(state_set(dptr, dlen, ptr+1, 20) == dlen); ptr += 21; } diff --git a/test/integration/firewall/firewallBase.test.ts b/test/integration/firewall/firewallBase.test.ts index de06cf1..039ec31 100644 --- a/test/integration/firewall/firewallBase.test.ts +++ b/test/integration/firewall/firewallBase.test.ts @@ -1,15 +1,12 @@ import { - Invoke, - // AccountSet, Payment, - // SetHook, + SetHook, SetHookFlags, TransactionMetadata, calculateHookOn, - // xrpToDrops, + xrpToDrops, } from '@transia/xrpl' import { IssuedCurrencyAmount } from '@transia/xrpl/dist/npm/models/common' -import { AccountID, Currency } from '@transia/ripple-binary-codec/dist/types' import { XrplIntegrationTestContext, serverUrl, @@ -20,26 +17,22 @@ import { Xrpld, ExecutionUtility, createHookPayload, - // floatToLEXfl, + floatToLEXfl, setHooksV3, SetHookParams, iHookParamEntry, iHookParamName, iHookParamValue, - formatAccountCurrencyBlob, - formatAccountBlob, - floatToLEXfl, + // formatAccountBlob, } from '@transia/hooks-toolkit' -// Firewall.Base: ACCEPT: passing sethook tt -// Firewall.Base: ACCEPT: amount < 0 -// Firewall.Base: ROLLBACK: block account - TODO -// Firewall.Base: ROLLBACK: block in tt -// Firewall.Base: ROLLBACK: block out tt -// Firewall.Base: ROLLBACK: block min xrp -// Firewall.Base: ROLLBACK: block max xrp -// Firewall.Base: ROLLBACK: block min usd -// Firewall.Base: ROLLBACK: block max xrp +// Firewall.Base: ACCEPT: Firewall: Ignoring SetHook txn +// Firewall.Base: ROLLBACK: Firewall: Blocklist match: TODO +// Firewall.Base: ROLLBACK: Firewall: blocking txn type (In): FIX +// Firewall.Base: ROLLBACK: Firewall: blocking txn type (Out): FIX +// Firewall.Base: ROLLBACK: Firewall: blocked incoming partial payment +// Firewall.Base: ROLLBACK: Firewall: Amount below threshold (XRP) +// Firewall.Base: ROLLBACK: Firewall: Amount below threshold (IC) // Firewall.Base: ACCEPT: Passthrough describe('firewall.base - Success Group', () => { @@ -50,135 +43,289 @@ describe('firewall.base - Success Group', () => { }) afterAll(async () => teardownClient(testContext)) - it('firewall base - ic whitelist', async () => { - const aliceWallet = testContext.alice - const bobWallet = testContext.bob - const elsaWallet = testContext.elsa - const elsaHex = AccountID.from(elsaWallet.classicAddress).toHex() - const currencyHex = Currency.from('USD').toHex() - const hook1Param1 = new iHookParamEntry( - new iHookParamName('FWP'), - new iHookParamValue(elsaHex, true) + it('firewall base - passing sethook tt', async () => { + const param1 = new iHookParamEntry( + new iHookParamName('FI'), + new iHookParamValue(calculateHookOn(['SetHook']), true) ) - const hook1Param2 = new iHookParamEntry( - new iHookParamName('FIC'), - new iHookParamValue(currencyHex, true) + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_base', + namespace: 'firewall_base', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['SetHook'], + hookParams: [param1.toXrpl()], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + + const aliceWallet = testContext.alice + const builtTx: SetHook = { + TransactionType: 'SetHook', + Account: aliceWallet.address, + Hooks: [ + { + Hook: createHookPayload({ + version: 0, + createFile: 'base', + namespace: 'base', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Payment'], + }), + }, + ], + } + + const result = await Xrpld.submit(testContext.client, { + wallet: aliceWallet, + tx: builtTx, + }) + + const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata ) - const hook1Param3 = new iHookParamEntry( - new iHookParamName('FIP'), - new iHookParamValue(elsaHex, true) + expect(hookEmitted.executions[0].HookReturnString).toMatch( + 'Firewall: Ignoring SetHook txn' ) - const hook1Param4 = new iHookParamEntry( + }) + + it('firewall base - block in tt', async () => { + const param1 = new iHookParamEntry( new iHookParamName('FI'), - new iHookParamValue(calculateHookOn(['Payment']), true) - ) - const hook1Param5 = new iHookParamEntry( - new iHookParamName('FLD'), - new iHookParamValue(floatToLEXfl('1'), true) - ) - const hook1Param6 = new iHookParamEntry( - new iHookParamName('FUD'), - new iHookParamValue(floatToLEXfl('100'), true) + new iHookParamValue(calculateHookOn(['SetHook']), true) ) - const hook1 = createHookPayload({ + console.log(param1.toXrpl()) + + const hook = createHookPayload({ version: 0, createFile: 'firewall_base', namespace: 'firewall_base', flags: SetHookFlags.hsfOverride, hookOnArray: ['Payment'], - hookParams: [ - hook1Param1.toXrpl(), - hook1Param2.toXrpl(), - hook1Param3.toXrpl(), - hook1Param4.toXrpl(), - hook1Param5.toXrpl(), - hook1Param6.toXrpl(), - ], + hookParams: [param1.toXrpl()], }) await setHooksV3({ client: testContext.client, seed: testContext.alice.seed, - hooks: [{ Hook: hook1 }], + hooks: [{ Hook: hook }], } as SetHookParams) - const hook2 = createHookPayload({ + + try { + // PAYMENT IN + const aliceWallet = testContext.alice + const bobWallet = testContext.bob + const builtTx: Payment = { + TransactionType: 'Payment', + Account: bobWallet.classicAddress, + Destination: aliceWallet.classicAddress, + Amount: xrpToDrops(100), + } + await Xrpld.submit(testContext.client, { + wallet: bobWallet, + tx: builtTx, + }) + throw Error('Expected Failure') + } catch (error: unknown) { + if (error instanceof Error) { + expect(error.message).toMatch('Firewall: Txn type') + } + } + }) + + it('firewall base - block out tt', async () => { + const param1 = new iHookParamEntry( + new iHookParamName('FO'), + new iHookParamValue(calculateHookOn(['SetHook']), true) + ) + + const hook = createHookPayload({ version: 0, - createFile: 'firewall_provider', - namespace: 'firewall_provider', + createFile: 'firewall_base', + namespace: 'firewall_base', flags: SetHookFlags.hsfOverride, - hookOnArray: ['Invoke'], + hookOnArray: ['Payment'], + hookParams: [param1.toXrpl()], }) - hook2.HookNamespace = - '0000000000000000000000000000000000000000000000000000000000000000' await setHooksV3({ client: testContext.client, - seed: testContext.elsa.seed, - hooks: [{ Hook: hook2 }], + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], } as SetHookParams) - // INVOKE OUT - const tx1Param1 = new iHookParamEntry( - new iHookParamName('M'), - new iHookParamValue(floatToLEXfl('1'), true) - ) - const tx1blob = formatAccountBlob([bobWallet.classicAddress]) - const builtTx1: Invoke = { - TransactionType: 'Invoke', - Account: elsaWallet.classicAddress, - Blob: tx1blob, - HookParameters: [tx1Param1.toXrpl()], + try { + // PAYMENT OUT + const aliceWallet = testContext.alice + const carolWallet = testContext.carol + const builtTx: Payment = { + TransactionType: 'Payment', + Account: aliceWallet.classicAddress, + Destination: carolWallet.classicAddress, + Amount: xrpToDrops(100), + } + await Xrpld.submit(testContext.client, { + wallet: aliceWallet, + tx: builtTx, + }) + throw Error('Expected Failure') + } catch (error: unknown) { + if (error instanceof Error) { + expect(error.message).toMatch('Firewall: Txn type') + } } - const result1 = await Xrpld.submit(testContext.client, { - wallet: elsaWallet, - tx: builtTx1, - }) + }) - const hook1Emitted = await ExecutionUtility.getHookExecutionsFromMeta( - testContext.client, - result1.meta as TransactionMetadata + it('firewall base - Firewall: Amount below threshold (XRP)', async () => { + const param1 = new iHookParamEntry( + new iHookParamName('FI'), + new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) ) - expect(hook1Emitted.executions[0].HookReturnString).toMatch('') - - // INVOKE OUT - const tx2Param1 = new iHookParamEntry( - new iHookParamName('M'), - new iHookParamValue(floatToLEXfl('2'), true) + const param2 = new iHookParamEntry( + new iHookParamName('FD'), + new iHookParamValue(floatToLEXfl('100'), true) ) - const tx2blob = formatAccountCurrencyBlob('USD', [ - testContext.gw.classicAddress, - ]) - const builtTx2: Invoke = { - TransactionType: 'Invoke', - Account: elsaWallet.classicAddress, - Blob: tx2blob, - HookParameters: [tx2Param1.toXrpl()], - } - const result2 = await Xrpld.submit(testContext.client, { - wallet: elsaWallet, - tx: builtTx2, + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_base', + namespace: 'firewall_base', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Payment'], + hookParams: [param1.toXrpl(), param2.toXrpl()], }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) - const hook2Emitted = await ExecutionUtility.getHookExecutionsFromMeta( - testContext.client, - result2.meta as TransactionMetadata + try { + // PAYMENT IN + const aliceWallet = testContext.alice + const carolWallet = testContext.carol + const builtTx: Payment = { + TransactionType: 'Payment', + Account: carolWallet.classicAddress, + Destination: aliceWallet.classicAddress, + Amount: xrpToDrops(99), + } + await Xrpld.submit(testContext.client, { + wallet: carolWallet, + tx: builtTx, + }) + throw Error('Expected Failure') + } catch (error: unknown) { + if (error instanceof Error) { + console.log(error.message) + expect(error.message).toMatch('Firewall: Amount below threshold') + } + } + }) + + it('firewall base - Firewall: Amount below threshold (IC)', async () => { + const param1 = new iHookParamEntry( + new iHookParamName('FI'), + new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + ) + const param2 = new iHookParamEntry( + new iHookParamName('FT'), + new iHookParamValue(floatToLEXfl('100'), true) ) - expect(hook2Emitted.executions[0].HookReturnString).toMatch('') - // PAYMENT IN + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_base', + namespace: 'firewall_base', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Payment'], + hookParams: [param1.toXrpl(), param2.toXrpl()], + }) + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + const amount: IssuedCurrencyAmount = { - value: '100', + value: '99', currency: 'USD', issuer: testContext.gw.classicAddress, } + + try { + // PAYMENT IN + const aliceWallet = testContext.alice + const carolWallet = testContext.carol + const builtTx: Payment = { + TransactionType: 'Payment', + Account: carolWallet.classicAddress, + Destination: aliceWallet.classicAddress, + Amount: amount, + } + await Xrpld.submit(testContext.client, { + wallet: carolWallet, + tx: builtTx, + }) + throw Error('Expected Failure') + } catch (error: unknown) { + if (error instanceof Error) { + expect(error.message).toMatch('Firewall: Amount below threshold') + } + } + }) + + it('firewall - passthrough', async () => { + const param1 = new iHookParamEntry( + new iHookParamName('FI'), + new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) + ) + const param2 = new iHookParamEntry( + new iHookParamName('FO'), + new iHookParamValue(calculateHookOn(['SetHook']), true) + ) + const param3 = new iHookParamEntry( + new iHookParamName('FD'), + new iHookParamValue(floatToLEXfl('99'), true) + ) + const param4 = new iHookParamEntry( + new iHookParamName('FT'), + new iHookParamValue(floatToLEXfl('99'), true) + ) + + const hook = createHookPayload({ + version: 0, + createFile: 'firewall_base', + namespace: 'firewall_base', + flags: SetHookFlags.hsfOverride, + hookOnArray: ['Payment'], + hookParams: [ + param1.toXrpl(), + param2.toXrpl(), + param3.toXrpl(), + param4.toXrpl(), + ], + }) + + await setHooksV3({ + client: testContext.client, + seed: testContext.alice.seed, + hooks: [{ Hook: hook }], + } as SetHookParams) + + // PAYMENT IN + const aliceWallet = testContext.alice + const carolWallet = testContext.carol const builtTx: Payment = { TransactionType: 'Payment', - Account: bobWallet.classicAddress, + Account: carolWallet.classicAddress, Destination: aliceWallet.classicAddress, - Amount: amount, + Amount: xrpToDrops(99), } - const result = await Xrpld.submit(testContext.client, { - wallet: bobWallet, + wallet: carolWallet, tx: builtTx, }) @@ -187,437 +334,7 @@ describe('firewall.base - Success Group', () => { result.meta as TransactionMetadata ) expect(hookEmitted.executions[0].HookReturnString).toMatch( - 'Firewall: Passing SetHook txn' + 'Firewall: Txn within thresholds' ) }) - - // it('firewall base - passing sethook tt', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook']), true) - // ) - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['SetHook'], - // [param1.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // const aliceWallet = testContext.alice - // const builtTx: SetHook = { - // TransactionType: 'SetHook', - // Account: aliceWallet.address, - // Hooks: [ - // { - // Hook: createHookPayload(0, 'base', 'base', SetHookFlags.hsfOverride, [ - // 'Payment', - // ]), - // }, - // ], - // } - - // const result = await Xrpld.submit(testContext.client, { - // wallet: aliceWallet, - // tx: builtTx, - // }) - - // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( - // testContext.client, - // result.meta as TransactionMetadata - // ) - // expect(hookEmitted.executions[0].HookReturnString).toMatch( - // 'Firewall: Passing SetHook txn' - // ) - // }) - - // it('firewall base - amount < 0', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook', 'AccountSet']), true) - // ) - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['AccountSet'], - // [param1.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // // ACCOUNT SET OUT - // const aliceWallet = testContext.alice - // const builtTx: AccountSet = { - // TransactionType: 'AccountSet', - // Account: aliceWallet.address, - // } - // const result = await Xrpld.submit(testContext.client, { - // wallet: aliceWallet, - // tx: builtTx, - // }) - - // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( - // testContext.client, - // result.meta as TransactionMetadata - // ) - // expect(hookEmitted.executions[0].HookReturnString).toMatch( - // 'Firewall: Ignoring negative amount' - // ) - // }) - - // it('firewall base - block in tt', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook']), true) - // ) - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // try { - // // PAYMENT IN - // const aliceWallet = testContext.alice - // const bobWallet = testContext.bob - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: bobWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // Amount: xrpToDrops(100), - // } - // await Xrpld.submit(testContext.client, { - // wallet: bobWallet, - // tx: builtTx, - // }) - // } catch (error: unknown) { - // if (error instanceof Error) { - // expect(error.message).toMatch('Firewall: blocking txn type') - // } - // } - // }) - - // it('firewall base - block out tt', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FO'), - // new iHookParamValue(calculateHookOn(['SetHook']), true) - // ) - - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // try { - // // PAYMENT OUT - // const aliceWallet = testContext.alice - // const carolWallet = testContext.carol - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: aliceWallet.classicAddress, - // Destination: carolWallet.classicAddress, - // Amount: xrpToDrops(100), - // } - // await Xrpld.submit(testContext.client, { - // wallet: aliceWallet, - // tx: builtTx, - // }) - // } catch (error: unknown) { - // if (error instanceof Error) { - // expect(error.message).toMatch('Firewall: blocking txn type') - // } - // } - // }) - - // it('firewall base - block min xrp', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) - // ) - // const param2 = new iHookParamEntry( - // new iHookParamName('FLD'), - // new iHookParamValue(floatToLEXfl('100'), true) - // ) - - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl(), param2.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // try { - // // PAYMENT IN - // const aliceWallet = testContext.alice - // const carolWallet = testContext.carol - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: carolWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // Amount: xrpToDrops(99), - // } - // await Xrpld.submit(testContext.client, { - // wallet: carolWallet, - // tx: builtTx, - // }) - // throw Error('Expected Failure') - // } catch (error: unknown) { - // if (error instanceof Error) { - // console.log(error.message) - // expect(error.message).toMatch( - // 'Firewall: blocking amount below threshold' - // ) - // } - // } - // }) - - // it('firewall base - block max xrp', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) - // ) - // const param2 = new iHookParamEntry( - // new iHookParamName('FLD'), - // new iHookParamValue(floatToLEXfl('10'), true) - // ) - // const param3 = new iHookParamEntry( - // new iHookParamName('FUD'), - // new iHookParamValue(floatToLEXfl('100'), true) - // ) - - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl(), param2.toXrpl(), param3.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // try { - // // PAYMENT IN - // const aliceWallet = testContext.alice - // const carolWallet = testContext.carol - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: carolWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // Amount: xrpToDrops(101), - // } - // await Xrpld.submit(testContext.client, { - // wallet: carolWallet, - // tx: builtTx, - // }) - // throw Error('Expected Failure') - // } catch (error: unknown) { - // if (error instanceof Error) { - // expect(error.message).toMatch( - // 'Firewall: blocking amount above threshold' - // ) - // } - // } - // }) - - // it('firewall base - block min amount', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) - // ) - // const param2 = new iHookParamEntry( - // new iHookParamName('FLT'), - // new iHookParamValue(floatToLEXfl('100'), true) - // ) - - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl(), param2.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // const amount: IssuedCurrencyAmount = { - // value: '99', - // currency: 'USD', - // issuer: testContext.gw.classicAddress, - // } - - // try { - // // PAYMENT IN - // const aliceWallet = testContext.alice - // const carolWallet = testContext.carol - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: carolWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // Amount: amount, - // } - // await Xrpld.submit(testContext.client, { - // wallet: carolWallet, - // tx: builtTx, - // }) - // } catch (error: unknown) { - // if (error instanceof Error) { - // expect(error.message).toMatch( - // 'Firewall: blocking amount below threshold' - // ) - // } - // } - // }) - // it('firewall base - block max amount', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) - // ) - // const param2 = new iHookParamEntry( - // new iHookParamName('FLT'), - // new iHookParamValue(floatToLEXfl('10'), true) - // ) - // const param3 = new iHookParamEntry( - // new iHookParamName('FUT'), - // new iHookParamValue(floatToLEXfl('100'), true) - // ) - - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl(), param2.toXrpl(), param3.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // const amount: IssuedCurrencyAmount = { - // value: '101', - // currency: 'USD', - // issuer: testContext.gw.classicAddress, - // } - // try { - // // PAYMENT IN - // const aliceWallet = testContext.alice - // const carolWallet = testContext.carol - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: carolWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // Amount: amount, - // } - // await Xrpld.submit(testContext.client, { - // wallet: carolWallet, - // tx: builtTx, - // }) - // } catch (error: unknown) { - // if (error instanceof Error) { - // expect(error.message).toMatch( - // 'Firewall: blocking amount above threshold' - // ) - // } - // } - // }) - - // it('firewall - passthrough', async () => { - // const param1 = new iHookParamEntry( - // new iHookParamName('FI'), - // new iHookParamValue(calculateHookOn(['SetHook', 'Payment']), true) - // ) - // const param2 = new iHookParamEntry( - // new iHookParamName('FO'), - // new iHookParamValue(calculateHookOn(['SetHook']), true) - // ) - // const param3 = new iHookParamEntry( - // new iHookParamName('FLD'), - // new iHookParamValue(floatToLEXfl('1'), true) - // ) - // const param4 = new iHookParamEntry( - // new iHookParamName('FUD'), - // new iHookParamValue(floatToLEXfl('100'), true) - // ) - - // const hook = createHookPayload( - // 0, - // 'firewall_base', - // 'firewall_base', - // SetHookFlags.hsfOverride, - // ['Payment'], - // [param1.toXrpl(), param2.toXrpl(), param3.toXrpl(), param4.toXrpl()] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // // PAYMENT IN - // const aliceWallet = testContext.alice - // const carolWallet = testContext.carol - // const builtTx: Payment = { - // TransactionType: 'Payment', - // Account: carolWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // Amount: xrpToDrops(100), - // } - // const result = await Xrpld.submit(testContext.client, { - // wallet: carolWallet, - // tx: builtTx, - // }) - - // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( - // testContext.client, - // result.meta as TransactionMetadata - // ) - // expect(hookEmitted.executions[0].HookReturnString).toMatch( - // 'Firewall: Passing txn within thresholds' - // ) - // }) }) diff --git a/test/integration/firewall/firewallProvider.test.ts b/test/integration/firewall/firewallProvider.test.ts index d024be9..f193850 100644 --- a/test/integration/firewall/firewallProvider.test.ts +++ b/test/integration/firewall/firewallProvider.test.ts @@ -1,12 +1,11 @@ import { Invoke, LedgerEntryRequest, - // Payment, + Payment, SetHookFlags, TransactionMetadata, - // xrpToDrops, + xrpToDrops, } from '@transia/xrpl' -import { AccountID } from '@transia/ripple-binary-codec/dist/types' import { HookDefinition as LeHookDefinition, Hook as LeHook, @@ -28,9 +27,9 @@ import { padHexString, // padHexString, } from '@transia/hooks-toolkit' +import { xrpAddressToHex } from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models' // Firewall.Provider: ACCEPT: TT != Invoke -// Firewall.Provider: ACCEPT: TX ACCOUNT != HOOK // Firewall.Provider: ACCEPT: TX Destination != HOOK // Firewall.Provider: ACCEPT: Add Account @@ -41,15 +40,11 @@ import { // Firewall.Provider: ACCEPT: Test Max - TODO // Firewall.Provider: ACCEPT: Test Fee for exponential growth - TODO -describe('Application.firewall_provider - Success Group', () => { +describe('firewall provider - Success Group', () => { let testContext: XrplIntegrationTestContext beforeAll(async () => { testContext = await setupClient(serverUrl) - }) - afterAll(async () => teardownClient(testContext)) - - it('firewall base - tt != invoke', async () => { const hook = createHookPayload({ version: 0, createFile: 'firewall_provider', @@ -62,7 +57,10 @@ describe('Application.firewall_provider - Success Group', () => { seed: testContext.alice.seed, hooks: [{ Hook: hook }], } as SetHookParams) + }) + afterAll(async () => teardownClient(testContext)) + it('firewall provider - tt != invoke', async () => { // PAYMENT IN const aliceWallet = testContext.alice const bobWallet = testContext.bob @@ -77,62 +75,11 @@ describe('Application.firewall_provider - Success Group', () => { tx: builtTx, }) - const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( - testContext.client, - result.meta as TransactionMetadata - ) - expect(hookEmitted.executions[0].HookReturnString).toMatch('') + const metadata = result.meta as TransactionMetadata + expect(metadata.HookExecutions).toBe(undefined) }) - // INVALID FAIL - // it('firewall base - tx account != hook', async () => { - // const hook = createHookPayload( - // 0, - // 'firewall_provider', - // 'firewall_provider', - // SetHookFlags.hsfOverride, - // ['Invoke'] - // ) - // await setHooksV3({ - // client: testContext.client, - // seed: testContext.alice.seed, - // hooks: [{ Hook: hook }], - // } as SetHookParams) - - // // INVOKE IN - // const aliceWallet = testContext.alice - // const bobWallet = testContext.bob - // const builtTx: Invoke = { - // TransactionType: 'Invoke', - // Account: bobWallet.classicAddress, - // Destination: aliceWallet.classicAddress, - // } - // const result = await Xrpld.submit(testContext.client, { - // wallet: aliceWallet, - // tx: builtTx, - // }) - - // const hookEmitted = await ExecutionUtility.getHookExecutionsFromMeta( - // testContext.client, - // result.meta as TransactionMetadata - // ) - // expect(hookEmitted.executions[0].HookReturnString).toMatch('') - // }) - - it('firewall base - tx dest != hook', async () => { - const hook = createHookPayload({ - version: 0, - createFile: 'firewall_provider', - namespace: 'firewall_provider', - flags: SetHookFlags.hsfOverride, - hookOnArray: ['Invoke'], - }) - await setHooksV3({ - client: testContext.client, - seed: testContext.alice.seed, - hooks: [{ Hook: hook }], - } as SetHookParams) - + it('firewall provider - tx dest != hook', async () => { // INVOKE OUT const aliceWallet = testContext.alice const bobWallet = testContext.bob @@ -153,19 +100,6 @@ describe('Application.firewall_provider - Success Group', () => { }) it('firewall provider - add account state', async () => { - const hook = createHookPayload({ - version: 0, - createFile: 'firewall_provider', - namespace: 'firewall_provider', - flags: SetHookFlags.hsfOverride, - hookOnArray: ['Invoke'], - }) - await setHooksV3({ - client: testContext.client, - seed: testContext.alice.seed, - hooks: [{ Hook: hook }], - } as SetHookParams) - // INVOKE OUT const aliceWallet = testContext.alice const blob = formatAccountBlob([testContext.carol.classicAddress]) @@ -178,6 +112,12 @@ describe('Application.firewall_provider - Success Group', () => { wallet: aliceWallet, tx: builtTx, }) + const hookExecutions = await ExecutionUtility.getHookExecutionsFromMeta( + testContext.client, + result.meta as TransactionMetadata + ) + // DONEEMPTY + expect(hookExecutions.executions[0].HookReturnString).toEqual('') const leHook = await StateUtility.getHook( testContext.client, @@ -189,40 +129,19 @@ describe('Application.firewall_provider - Success Group', () => { } const hookDefRes = await testContext.client.request(hookDefRequest) const leHookDef = hookDefRes.result.node as LeHookDefinition - - const aliceAccHex = AccountID.from(aliceWallet.classicAddress).toHex() const hookState = await StateUtility.getHookState( testContext.client, testContext.alice.classicAddress, - padHexString(aliceAccHex), + padHexString(xrpAddressToHex(testContext.carol.classicAddress)), leHookDef.HookNamespace as string ) - const hookExecutions = await ExecutionUtility.getHookExecutionsFromMeta( - testContext.client, - result.meta as TransactionMetadata - ) - // DONEEMPTY - expect(hookExecutions.executions[0].HookReturnString).toEqual('') expect(hookState.HookStateData).toBeDefined() }) it('firewall provider - remove account state', async () => { - const hook = createHookPayload({ - version: 0, - createFile: 'firewall_provider', - namespace: 'firewall_provider', - flags: SetHookFlags.hsfOverride, - hookOnArray: ['Invoke'], - }) - await setHooksV3({ - client: testContext.client, - seed: testContext.alice.seed, - hooks: [{ Hook: hook }], - } as SetHookParams) - // INVOKE OUT const aliceWallet = testContext.alice - const blob = formatAccountBlob([testContext.carol.classicAddress]) + const blob = formatAccountBlob([], [testContext.carol.classicAddress]) const builtTx: Invoke = { TransactionType: 'Invoke', Account: aliceWallet.classicAddress, @@ -255,15 +174,13 @@ describe('Application.firewall_provider - Success Group', () => { } const hookDefRes = await testContext.client.request(hookDefRequest) const leHookDef = hookDefRes.result.node as LeHookDefinition - - const aliceAccHex = AccountID.from(aliceWallet.classicAddress).toHex() - const hookState = await StateUtility.getHookState( + await StateUtility.getHookState( testContext.client, testContext.alice.classicAddress, - padHexString(aliceAccHex), + padHexString(xrpAddressToHex(testContext.carol.classicAddress)), leHookDef.HookNamespace as string ) - expect(hookState.HookStateData).toBeDefined() + throw Error('Expected Failure') } catch (error: unknown) { if (error instanceof Error) { expect(error.message).toEqual('entryNotFound')