Files
xahaud/hook/utils-tests.js
2023-04-06 08:19:59 +00:00

634 lines
24 KiB
JavaScript

const fs = require('fs')
const xrpljs = require('xrpl-hooks');
const kp = require('ripple-keypairs');
const crypto = require('crypto')
const rbc = require('xrpl-binary-codec')
const rac = require('ripple-address-codec');
const err = (x) =>
{
console.log(x); process.exit(1);
}
// Fails via process.exit
module.exports = {
TestRig: (endpoint)=>
{
return new Promise((resolve, reject)=>
{
const api = new xrpljs.Client(endpoint);
const nftid = (acc, flags, fee, taxon, mintseq) =>
{
if (typeof(acc.classicAddress) != "undefined")
acc = acc.classicAddress;
acc = rac.decodeAccountID(acc);
const ts = mintseq;
const tax =(taxon ^ ((384160001 * ts) + 2459));
const id = Buffer.from([
(flags >> 8) & 0xFF,
flags & 0xFF,
(fee >> 8) & 0xFF,
fee & 0xFF,
acc[0],
acc[1],
acc[2],
acc[3],
acc[4],
acc[5],
acc[6],
acc[7],
acc[8],
acc[9],
acc[10],
acc[11],
acc[12],
acc[13],
acc[14],
acc[15],
acc[16],
acc[17],
acc[18],
acc[19],
(tax >> 24) & 0xFF,
(tax >> 16) & 0xFF,
(tax >> 8) & 0xFF,
tax & 0xFF,
(ts >> 24) & 0xFF,
(ts >> 16) & 0xFF,
(ts >> 8) & 0xFF,
ts & 0xFF
], 'binary').toString('hex').toUpperCase()
return id;
};
const fee = (tx_blob) =>
{
return new Promise((resolve, reject) =>
{
let req = {command: 'fee'};
if (tx_blob)
req['tx_blob'] = tx_blob;
api.request(req).then(resp =>
{
resolve(resp.result.drops);
}).catch(e =>
{
reject(e);
});
});
};
const ledgerAccept = (n) =>
{
return new Promise((resolve, reject) =>
{
const la = (remaining) =>
{
let req = {command: 'ledger_accept'};
api.request(req).then(resp =>
{
if (remaining <= 0)
resolve(resp);
la(remaining - 1);
}).catch(e=>reject(e));
};
la(typeof(n) == 'undefined' ? 1 : n);
});
};
const assertTxnSuccess = x =>
{
if (!x || !x.result || x.result.engine_result_code != 0)
{
console.log("Transaction failed:", x)
process.exit(1);
}
};
const assert = (x, m) =>
{
if (!(x))
{
console.log("Assertion failed: ", m);
console.log(new Error().stack);
process.exit(1);
}
};
const fetchMeta = (hash) =>
{
if (typeof(hash) != 'string')
hash = hash.result.tx_json.hash
return new Promise((resolve, reject) =>
{
api.request(
{
command:"tx",
transaction: hash
}).then(e=>{
resolve(e.result.meta)
}).catch(e=>reject(e));
});
};
const fetchMetaHookExecutions = (hash, hookhash) =>
{
return new Promise((resolve, reject) =>
{
fetchMeta(hash).then(m=>
{
if (typeof(m) == 'undefined' ||
typeof(m.HookExecutions) == 'undefined' ||
typeof(m.HookExecutions.length) == 'undefined')
{
return resolve([])
}
let ret = [];
for (let i = 0; i < m.HookExecutions.length; ++i)
{
if (typeof(hookhash) == 'undefined' ||
m.HookExecutions[i].HookExecution.HookHash == hookhash)
m.HookExecutions[i].HookExecution.HookReturnCode =
parseInt(m.HookExecutions[i].HookExecution.HookReturnCode, 16);
m.HookExecutions[i].HookExecution.HookInstructionCount =
parseInt(m.HookExecutions[i].HookExecution.HookInstructionCount, 16);
let s = m.HookExecutions[i].HookExecution.HookReturnString;
if (s != '')
m.HookExecutions[i].HookExecution.HookReturnString =
Buffer.from(s, 'hex').toString('utf-8')
ret.push(m.HookExecutions[i].HookExecution);
}
resolve(ret);
}).catch(e=>reject(e));
});
};
const assertTxnFailure = x =>
{
if (!x || !x.result || x.result.engine_result_code == 0)
{
console.log("Transaction failed:", x)
process.exit(1);
}
};
const wasm = (x) =>
{
console.log('wasm(' + x + ')');
try
{
return fs.readFileSync(x).toString('hex').toUpperCase();
}
catch (e) {}
try
{
return fs.readFileSync('' + x).toString('hex').toUpperCase();
}
catch (e) {}
console.log("Could not find " + x)
process.exit(1);
};
const wasmHash = (x)=>
{
const blob = wasm(x);
return crypto.createHash('SHA512').
update(Buffer.from(blob, 'hex')).
digest().slice(0,32).toString('hex').toUpperCase();
}
const feeCompute = (account_seed, txn_org) =>
{
return new Promise((resolve, reject) =>
{
txn_to_send = { ... txn_org };
txn_to_send['SigningPubKey'] = '';
let wal = xrpljs.Wallet.fromSeed(account_seed);
api.prepareTransaction(txn_to_send, {wallet: wal}).then(txn =>
{
let ser = rbc.encode(txn);
fee(ser).then(fees =>
{
let base_drops = fees.base_fee
delete txn_to_send['SigningPubKey']
txn_to_send['Fee'] = base_drops + '';
api.request(
{
command: "account_info",
account: txn.Account
}).then(y=>
{
let seq = (y.result.account_data.Sequence);
txn_to_send.Sequence = seq;
api.prepareTransaction(txn_to_send, {wallet: wal}).then(txn =>
{
resolve(txn);
}).catch(e=>{reject(e);});
}).catch(e=>{reject(e);});
}).catch(e=>{reject(e);});
}).catch(e=>{reject(e);});
});
}
const feeSubmitAccept = (seed, txn) =>
{
return new Promise((resolve, reject) =>
{
feeSubmit(seed, txn).then(x=>
{
ledgerAccept().then(()=>
{
resolve(x);
}).catch(e=>
{
reject(e);
});
}).catch(e =>
{
reject(e);
});
});
}
const submit = (seed, txn) =>
{
return new Promise((resolve, reject) =>
{
api.submit(txn,
{wallet: xrpljs.Wallet.fromSeed(seed)}).then(s=>
{
resolve(s);
}).catch(e=>{reject(e);});
});
}
const feeSubmit = (seed, txn) =>
{
return new Promise((resolve, reject) =>
{
feeCompute(seed, txn).then(txn=>
{
api.submit(txn,
{wallet: xrpljs.Wallet.fromSeed(seed)}).then(s=>
{
resolve(s);
}).catch(e=>{reject(e);});
}).catch(e=>{reject(e);});
});
}
const genesisseed = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
const genesisaddr = 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh';
const genesis = xrpljs.Wallet.fromSeed(genesisseed);
const randomAccount = ()=>
{
const acc = xrpljs.Wallet.fromSeed(kp.generateSeed());
return acc
};
const fromSeed = (x)=>
{
const acc = xrpljs.Wallet.fromSeed(x);
return acc
};
const pay_mock = (seed, amt, dest) =>
{
if (dest.classicAddress != undefined)
dest = dest.classicAddress;
return new Promise((resolve, reject) =>
{
let wal = xrpljs.Wallet.fromSeed(seed);
api.prepareTransaction({
Account: wal.classicAddress,
TransactionType: "Payment",
Amount: ''+amt,
Destination: dest,
SigningPubKey: ''
}, {wallet: wal}).then(txn =>
{
resolve(rbc.encode(txn));
}).catch(e=>
{
reject(e);
});
});
}
const pay = (seed, amt, dest) =>
{
if (dest.classicAddress != undefined)
dest = dest.classicAddress;
return new Promise((resolve, reject) =>
{
let wal = xrpljs.Wallet.fromSeed(seed);
feeSubmit(seed, {
Account: wal.classicAddress,
TransactionType: "Payment",
Amount: ''+amt,
Destination: dest
}).then(x=>
{
assertTxnSuccess(x);
resolve(x);
}).catch(err);
});
};
const hookHash = fn =>
{
let b = fs.readFileSync('' + fn);
return crypto.createHash('SHA512').update(b).digest().slice(0,32).toString('hex').toUpperCase()
}
const fundFromGenesis = (acc) =>
{
return new Promise((resolve, reject) =>
{
const ffg = (acc, after) =>
{
if (typeof(acc) != 'string')
acc = acc.classicAddress;
console.log('ffg: ' + acc);
feeSubmitAccept(genesis.seed, {
Account: genesis.classicAddress, // fund account from genesis
TransactionType: "Payment",
Amount: "100000000000",
Destination: acc,
}).then(x=>
{
assertTxnSuccess(x);
if (after)
return after();
else
resolve();
}).catch(err);
};
const doFfg = (acc) =>
{
if (typeof(acc.length) == 'undefined')
return ffg(acc);
else if (acc.length == 1)
return ffg(acc[0]);
else
{
return ffg(acc[0],
((acc)=>{
return ()=>{
acc.shift();
return doFfg(acc);
};
})(acc));
}
}
return doFfg(acc);
});
};
const trustSet = (issuer, currency, limit, holders) =>
{
if (typeof(issuer.classicAddress) != 'undefined')
issuer = issuer.classicAddress;
return new Promise((resolve, reject)=>
{
const doTs = (holder) =>
{
if (holder.length == 0)
return resolve();
let h = holder.shift();
feeSubmitAccept(h.seed,
{
Account: h.classicAddress,
TransactionType: "TrustSet",
LimitAmount: {
"currency": currency + "",
"issuer": issuer,
"value": limit + ""
}
}).then(x=>
{
console.log(x)
assertTxnSuccess(x);
return doTs(holder);
}).catch(e=>reject(e));
};
doTs(holders);
});
};
const issueTokens = (issuer, currency, toWhom) =>
{
return new Promise((resolve, reject) =>
{
const itf = (issuer, currency, toWhom) =>
{
let c = 0;
for (let next in toWhom)
{
c++;
let addr = next;
let amt = toWhom[addr];
delete toWhom[addr];
let txn =
{
Account: issuer.classicAddress,
TransactionType: "Payment",
Amount: {
"currency": currency,
"value": amt + "",
"issuer": issuer.classicAddress
},
Destination: addr
};
feeSubmitAccept(issuer.seed, txn).then(x=>
{
console.log(x);
assertTxnSuccess(x);
return itf(issuer, currency, toWhom);
}).catch(e=>reject(e));
break;
}
if (c == 0)
resolve();
};
return itf(issuer, currency, toWhom);
});
};
const setTshCollect = (accounts) =>
{
return new Promise((resolve, reject) =>
{
const stc = (accounts) =>
{
if (accounts.length == 0)
return resolve();
let acc = accounts.shift();
feeSubmitAccept(acc.seed,
{
Account: acc.classicAddress,
TransactionType: "AccountSet",
SetFlag: 11
}).then(x=>
{
console.log(x);
assertTxnSuccess(x);
return stc(accounts);
}).catch(e=>reject(e));
};
stc(accounts);
});
}
const feeSubmitAcceptMultiple = (txn, accounts) =>
{
return new Promise((resolve, reject) =>
{
const stc = (accounts) =>
{
if (accounts.length == 0)
return resolve();
let acc = accounts.shift();
let txn_to_submit = { ... txn };
txn_to_submit['Account'] = acc.classicAddress;
feeSubmitAccept(acc.seed, txn_to_submit).then(x=>
{
console.log(x);
assertTxnSuccess(x);
return stc(accounts);
}).catch(e=>reject(e));
};
stc(accounts);
});
}
const log = m =>
{
// console.log(JSON.stringify(m, null, 4));
console.dir(m, {depth:null});
}
const hex_memos = (x) =>
{
if (!("Memos" in x))
return;
for (y in x["Memos"])
{
for (a in x["Memos"][y])
{
let Fields = ["MemoFormat", "MemoType", "MemoData"];
for (z in Fields)
{
if (Fields[z] in x["Memos"][y][a])
{
let u = x["Memos"][y][a][Fields[z]].toUpperCase()
if (u.match(/^[0-9A-F]+$/))
{
x["Memos"][y][a][Fields[z]] = u;
continue;
}
x["Memos"][y][a][Fields[z]] =
""+Buffer.from(x["Memos"][y][a][Fields[z]]).toString('hex').toUpperCase();
}
}
}
}
}
api.connect().then(()=>
{
resolve({
hex_memos: hex_memos,
rbc: rbc,
rac: rac,
api: api,
xrpljs: xrpljs,
assertTxnSuccess: assertTxnSuccess,
assertTxnFailure: assertTxnFailure,
wasm: wasm,
kp: kp,
genesis: genesis,
randomAccount: randomAccount,
fromSeed: fromSeed,
fundFromGenesis: fundFromGenesis,
err: err,
hsfOVERRIDE: 1,
hsfNSDELETE: 2,
hsfCOLLECT: 4,
asfTshCollect: 11,
hookHash: hookHash,
pay: pay,
pay_mock: pay_mock,
fee: fee,
genesisseed: genesisseed,
genesisaddr: genesisaddr,
feeCompute: feeCompute,
feeSubmit: feeSubmit,
feeSubmitAccept: feeSubmitAccept,
ledgerAccept: ledgerAccept,
fetchMeta: fetchMeta,
fetchMetaHookExecutions: fetchMetaHookExecutions,
wasmHash: wasmHash,
assert: assert,
trustSet: trustSet,
issueTokens: issueTokens,
log: log,
submit: submit,
setTshCollect: setTshCollect,
feeSubmitAcceptMultiple: feeSubmitAcceptMultiple,
nftid: nftid
});
}).catch(err);
});
}
};