mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
519 lines
20 KiB
JavaScript
519 lines
20 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 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 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 = () =>
|
|
{
|
|
return new Promise((resolve, reject) =>
|
|
{
|
|
let req = {command: 'ledger_accept'};
|
|
api.request(req).then(resp =>
|
|
{
|
|
resolve(resp)
|
|
}).catch(e =>
|
|
{
|
|
reject(e);
|
|
});
|
|
});
|
|
};
|
|
|
|
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) =>
|
|
{
|
|
if (x.slice(0,1) != '/')
|
|
x = 'wasm/' + x;
|
|
return fs.readFileSync( x).toString('hex').toUpperCase();
|
|
};
|
|
|
|
|
|
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 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());
|
|
console.log(acc)
|
|
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);
|
|
|
|
api.submit({
|
|
Account: wal.classicAddress,
|
|
TransactionType: "Payment",
|
|
Amount: ''+amt,
|
|
Destination: dest,
|
|
Fee: "10000"
|
|
}, {wallet: wal}).then(x=>
|
|
{
|
|
assertTxnSuccess(x);
|
|
resolve(x);
|
|
}).catch(err);
|
|
});
|
|
};
|
|
|
|
const hookHash = fn =>
|
|
{
|
|
let b = fs.readFileSync('wasm/' + 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: "1000000000",
|
|
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});
|
|
}
|
|
|
|
api.connect().then(()=>
|
|
{
|
|
resolve({
|
|
rbc: rbc,
|
|
api: api,
|
|
xrpljs: xrpljs,
|
|
assertTxnSuccess: assertTxnSuccess,
|
|
assertTxnFailure: assertTxnFailure,
|
|
wasm: wasm,
|
|
kp: kp,
|
|
genesis: genesis,
|
|
randomAccount: randomAccount,
|
|
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,
|
|
setTshCollect: setTshCollect,
|
|
feeSubmitAcceptMultiple: feeSubmitAcceptMultiple
|
|
|
|
});
|
|
}).catch(err);
|
|
});
|
|
}
|
|
};
|