examples and tests WIP

This commit is contained in:
Richard Holland
2022-02-18 12:41:52 +00:00
parent e0fa4b4f1f
commit 9325e11b14
34 changed files with 2339 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
# HookSet Tests
This test set pertains to testing the functionality of installing / creating / updating / deleting hooks.
In short: everything except running hooks.

3
hookstests/hookset/TODO Normal file
View File

@@ -0,0 +1,3 @@
check imported function signatures when checking whitelist
test grants
test params

View File

@@ -0,0 +1,2 @@
all:
(cd wasm; make)

View File

@@ -0,0 +1,18 @@
{
"dependencies": {
"ripple-keypairs": "^1.1.3",
"ripple-lib": "^1.10.0",
"xrpl": "^2.1.1",
"xrpl-binary-codec": "^1.3.0",
"xrpl-hooks": "^2.1.1"
},
"name": "hookset",
"description": "This test set pertains to testing the functionality of installing / creating / updating / deleting hooks. In short: everything except running hooks.",
"version": "1.0.0",
"main": "''",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}

19
hookstests/hookset/pay.js Normal file
View File

@@ -0,0 +1,19 @@
if (process.argv.length < 5)
{
console.log("Usage: node pay <source family seed> <amount xrp> <destination account>")
process.exit(1)
}
const secret = process.argv[2];
const amount = BigInt(process.argv[3]) * 1000000n
const dest = process.argv[4];
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
t.pay(secret, amount, dest).then(x=>
{
console.log(x);
process.exit(0);
});
});

View File

@@ -0,0 +1,51 @@
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
const account = t.randomAccount();
t.fundFromGenesis(account).then(()=>
{
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: t.wasm('accept.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: t.wasm('accept.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnFailure(x)
console.log(x);
process.exit(0);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
})

View File

@@ -0,0 +1,66 @@
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
const account = t.randomAccount();
t.fundFromGenesis(account).then(()=>
{
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: "",
Flags: t.hfsOVERRIDE
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: t.wasm('accept.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: "", // hook delete
Flags: t.hfsOVERRIDE
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
process.exit(0);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
})

View File

@@ -0,0 +1,101 @@
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
const account = t.randomAccount();
const account2 = t.randomAccount();
t.fundFromGenesis(account).then(()=>
{
t.fundFromGenesis(account2).then(()=>
{
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{ Hook: {
CreateCode: t.wasm('checkstate.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x)
t.api.submit(
{
Account: account2.classicAddress,
TransactionType: "SetHook",
Fee: "100000",
Hooks: [
{ Hook: {
HookHash: "348C7966C79737F6254C3D3866DBEE0AE1584E0751771F0588F898E65C7DFB84",
Flags: 0
}},
{ Hook: {
}},
{ Hook: {
HookHash: "348C7966C79737F6254C3D3866DBEE0AE1584E0751771F0588F898E65C7DFB84"
}}
]
}, {wallet: account2}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
t.api.submit(
{
Account: account2.classicAddress,
TransactionType: "SetHook",
Fee: "100000",
Flags: 0,
Hooks: [
{ Hook: {
"Flags": t.hsfOVERRIDE,
"CreateCode": "",
}}
]
}, {wallet: account2}).then(x=>
{
console.log(x);
t.assertTxnSuccess(x);
t.api.submit(
{
Account: account2.classicAddress,
TransactionType: "SetHook",
Fee: "100000",
Flags: 0,
Hooks: [
{Hook:{}},
{Hook:{}},
{ Hook: {
HookParameters:
[
{HookParameter: {
HookParameterName: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
HookParameterValue: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"
}}
]
}}
]
}, {wallet: account2}).then(x=>
{
console.log(x);
t.assertTxnSuccess(x);
console.log("account 1 has the creation: ", account.classicAddress);
console.log("account 2 has the install and delete and update: ", account2.classicAddress);
process.exit(0);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
})

View File

@@ -0,0 +1,47 @@
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
const account = t.randomAccount();
t.fundFromGenesis(account).then(()=>
{
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: t.wasm('accept.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{ Hook: { } },
{ Hook: { } },
{ Hook: { } },
{ Hook: { } }
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnFailure(x)
console.log(x);
process.exit(0);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
})

View File

@@ -0,0 +1,69 @@
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
const account = t.randomAccount();
t.fundFromGenesis(account).then(()=>
{
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: t.wasm('makestate.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
},
{ Hook: {} },
{ Hook: {} },
{ Hook: {
CreateCode: t.wasm('checkstate.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "AccountSet", // trigger hooks
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Fee: "100000",
Hooks: [
{
Hook: {
Flags: t.hsfNSDELETE,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"
}
}
]
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
process.exit(0);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
})

View File

@@ -0,0 +1,49 @@
require('./utils-tests.js').TestRig('ws://localhost:6005').then(t=>
{
const account = t.randomAccount();
t.fundFromGenesis(account).then(()=>
{
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "SetHook",
Hooks: [
{
Hook: {
CreateCode: t.wasm('makestate.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
},
{ Hook: {} },
{ Hook: {} },
{ Hook: {
CreateCode: t.wasm('checkstate.wasm'),
HookApiVersion: 0,
HookNamespace: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
HookOn: "0000000000000000"
}
}
],
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
t.api.submit(
{
Account: account.classicAddress,
TransactionType: "AccountSet", // trigger hooks
Fee: "100000"
}, {wallet: account}).then(x=>
{
t.assertTxnSuccess(x)
console.log(x);
process.exit(0);
}).catch(t.err);
}).catch(t.err);
}).catch(t.err);
})

View File

@@ -0,0 +1,87 @@
const fs = require('fs')
const xrpljs = require('xrpl-hooks');
const kp = require('ripple-keypairs');
// Fails via process.exit
module.exports = {
TestRig: (endpoint)=>
{
return new Promise((resolve, reject)=>
{
const api = new xrpljs.Client(endpoint);
const assertTxnSuccess = x =>
{
if (!x || !x.result || x.result.engine_result_code != 0)
{
console.log("Transaction failed:", x)
process.exit(1);
}
};
const assertTxnFailure = x =>
{
if (!x || !x.result || x.result.engine_result_code == 0)
{
console.log("Transaction failed:", x)
process.exit(1);
}
};
const err = (x) =>
{
console.log(x); process.exit(1);
}
const wasm = (x) =>
{
return fs.readFileSync('wasm/' + x).toString('hex').toUpperCase();
}
const genesis = xrpljs.Wallet.fromSeed('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
const randomAccount = ()=>
{
return xrpljs.Wallet.fromSeed(kp.generateSeed());
};
const fundFromGenesis = (acc) =>
{
return new Promise((resolve, reject) =>
{
if (typeof(acc) != 'string')
acc = acc.classicAddress;
api.submit({
Account: genesis.classicAddress, // fund account from genesis
TransactionType: "Payment",
Amount: "1000000000",
Destination: acc
}, {wallet: genesis}).then(x=>
{
assertTxnSuccess(x);
resolve();
}).catch(err);
});
};
api.connect().then(()=>
{
resolve({
api: api,
xrpljs: xrpljs,
assertTxnSuccess: assertTxnSuccess,
assertTxnFailure: assertTxnFailure,
wasm: wasm,
kp: kp,
genesis: genesis,
randomAccount: randomAccount,
fundFromGenesis: fundFromGenesis,
err: err,
hsfOVERRIDE: 1,
hsfNSDELETE: 2
});
}).catch(err);
});
}
};

View File

@@ -0,0 +1,20 @@
/**
* This hook just accepts any transaction coming through it
*/
#include <stdint.h>
extern int32_t _g (uint32_t id, uint32_t maxiter);
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
int64_t cbak(uint32_t what)
{
return 0;
}
int64_t hook(uint32_t reserved )
{
accept (0,0,0);
_g(1,1); // every hook needs to import guard function and use it at least once
// unreachable
return 0;
}

View File

@@ -0,0 +1,42 @@
#include <stdint.h>
extern int32_t _g (uint32_t id, uint32_t maxiter);
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t state (uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len);
extern int64_t trace_num (uint32_t a, uint32_t b, uint64_t i);
#define SBUF(x) x, sizeof(x)
#define GUARD(n) _g(__LINE__, n+1)
int64_t cbak(uint32_t what)
{
return 0;
}
int64_t hook(uint32_t reserved )
{
uint8_t test[] = "hello world!";
uint8_t test_key[32];
for (int i = 0; GUARD(32), i < 32; ++i)
test_key[i] = i;
uint8_t buf[128];
int64_t result = state(SBUF(buf), SBUF(test_key));
if (result <= 0)
{
trace_num(SBUF("state call failed"), result);
rollback(SBUF("state call failed"), 2);
}
for (int i = 0; GUARD(sizeof(test)+1), i < sizeof(test); ++i)
if (test[i] != buf[i])
rollback(SBUF("hook state did not match expected value"), 1);
accept(SBUF("hook state matched expected value"),0);
_g(1,1); // every hook needs to import guard function and use it at least once
// unreachable
return 0;
}

View File

@@ -0,0 +1,8 @@
all: accept.wasm makestate.wasm checkstate.wasm
accept.wasm: accept.c
wasmcc accept.c -o accept.wasm -O0 -Wl,--allow-undefined -I../
makestate.wasm: makestate.c
wasmcc makestate.c -o makestate.wasm -O0 -Wl,--allow-undefined -I../
checkstate.wasm: checkstate.c
wasmcc checkstate.c -o checkstate.wasm -O0 -Wl,--allow-undefined -I../

View File

@@ -0,0 +1,31 @@
#include <stdint.h>
extern int32_t _g (uint32_t id, uint32_t maxiter);
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
extern int64_t state_set (uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len);
extern int64_t trace_num (uint32_t a, uint32_t b, uint64_t i);
#define SBUF(x) x, sizeof(x)
#define GUARD(n) _g(__LINE__, n+1)
int64_t cbak(uint32_t what)
{
return 0;
}
int64_t hook(uint32_t reserved )
{
uint8_t test[] = "hello world!";
trace_num(SBUF("location of test[]:"), test);
uint8_t test_key[32];
for (int i = 0; GUARD(32), i < 32; ++i)
test_key[i] = i;
int64_t result = state_set(SBUF(test), SBUF(test_key));
trace_num(SBUF("state_set result:"), result);
accept (0,0,0);
_g(1,1); // every hook needs to import guard function and use it at least once
// unreachable
return 0;
}