UNPKG

@ecash/lib

Version:

Library for eCash transaction building

183 lines 7.79 kB
"use strict"; // Copyright (c) 2024 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestRunner = void 0; const ecc_js_1 = require("../ecc.js"); const hash_js_1 = require("../hash.js"); const hex_js_1 = require("../io/hex.js"); const op_js_1 = require("../op.js"); const opcode_js_1 = require("../opcode.js"); const script_js_1 = require("../script.js"); const tx_js_1 = require("../tx.js"); const txBuilder_js_1 = require("../txBuilder.js"); const OP_TRUE_SCRIPT = script_js_1.Script.fromOps([opcode_js_1.OP_1]); const OP_TRUE_SCRIPT_SIG = script_js_1.Script.fromOps([ (0, op_js_1.pushBytesOp)(OP_TRUE_SCRIPT.bytecode), ]); // Like OP_TRUE_SCRIPT but much bigger to avoid undersize const ANYONE_SCRIPT = script_js_1.Script.fromOps([(0, op_js_1.pushBytesOp)((0, hex_js_1.fromHex)('01'.repeat(100)))]); const ANYONE_SCRIPT_SIG = script_js_1.Script.fromOps([(0, op_js_1.pushBytesOp)(ANYONE_SCRIPT.bytecode)]); class TestRunner { constructor(ecc, runner, chronik) { this.ecc = ecc; this.runner = runner; this.chronik = chronik; this.coinsTxid = undefined; this.lastUsedOutIdx = 0; } static async setup(setupScript = 'setup_scripts/ecash-lib_base') { const { ChronikClient } = await Promise.resolve().then(() => __importStar(require('chronik-client'))); const { spawn } = await Promise.resolve().then(() => __importStar(require('node:child_process'))); const events = await Promise.resolve().then(() => __importStar(require('node:events'))); const statusEvent = new events.EventEmitter(); const runner = spawn('python3', [ 'test/functional/test_runner.py', // Place the setup in the python file setupScript, ], { stdio: ['ipc'], // Needs to be set dynamically and the Bitcoin ABC // node has to be built first. cwd: process.env.BUILD_DIR || '.', }); // Redirect stdout so we can see the messages from the test runner runner.stdout?.pipe(process.stdout); runner.stderr?.pipe(process.stderr); runner.on('error', function (error) { console.log('Test runner error, aborting: ' + error); runner.kill(); process.exit(-1); }); runner.on('exit', function (code, signal) { // The test runner failed, make sure to propagate the error if (code !== null && code !== undefined && code != 0) { console.log('Test runner completed with code ' + code); process.exit(code); } // The test runner was aborted by a signal, make sure to return an // error if (signal !== null && signal !== undefined) { console.log('Test runner aborted by signal ' + signal); process.exit(-2); } // In all other cases, let the test return its own status as // expected }); runner.on('spawn', function () { console.log('Test runner started'); }); let chronik = undefined; runner.on('message', async function (message) { if (message && message.test_info && message.test_info.chronik) { console.log('Setting chronik url to ', message.test_info.chronik); chronik = new ChronikClient(message.test_info.chronik); } if (message && message.status) { while (!statusEvent.emit(message.status)) { await new Promise(resolve => setTimeout(resolve, 100)); } } }); const ecc = new ecc_js_1.Ecc(); // We got the coins, can fan out now await events.once(statusEvent, 'ready'); if (chronik === undefined) { throw new Event('Chronik is undefined'); } return new TestRunner(ecc, runner, chronik); } async setupCoins(numCoins, coinValue) { const opTrueScriptHash = (0, hash_js_1.shaRmd160)(OP_TRUE_SCRIPT.bytecode); const utxos = (await this.chronik.script('p2sh', (0, hex_js_1.toHex)(opTrueScriptHash)).utxos()).utxos; const anyoneScriptHash = (0, hash_js_1.shaRmd160)(ANYONE_SCRIPT.bytecode); const anyoneP2sh = script_js_1.Script.p2sh(anyoneScriptHash); const tx = new tx_js_1.Tx({ inputs: utxos.map(utxo => ({ prevOut: utxo.outpoint, script: OP_TRUE_SCRIPT_SIG, sequence: 0xffffffff, })), }); const utxosValue = utxos.reduce((a, b) => a + b.value, 0); for (let i = 0; i < numCoins; ++i) { tx.outputs.push({ value: coinValue, script: anyoneP2sh, }); } tx.outputs.push({ value: 0, script: script_js_1.Script.fromOps([opcode_js_1.OP_RETURN]), }); tx.outputs[tx.outputs.length - 1].value = utxosValue - numCoins * coinValue - tx.serSize(); this.coinsTxid = (await this.chronik.broadcastTx(tx.ser())).txid; this.coinValue = coinValue; } getOutpoint() { if (this.coinsTxid === undefined) { throw new Error('TestRunner.coinsTxid undefined, call setupCoins'); } return { txid: this.coinsTxid, outIdx: this.lastUsedOutIdx++, // use value, then increment }; } async sendToScript(value, script) { const coinValue = this.coinValue; const values = Array.isArray(value) ? value : [value]; const setupTxBuilder = new txBuilder_js_1.TxBuilder({ inputs: [ { input: { prevOut: this.getOutpoint(), script: ANYONE_SCRIPT_SIG, sequence: 0xffffffff, signData: { value: coinValue, }, }, }, ], outputs: [ ...values.map(value => ({ value, script })), script_js_1.Script.fromOps([opcode_js_1.OP_RETURN]), // burn leftover ], }); const setupTx = setupTxBuilder.sign(this.ecc, 1000, 546); return (await this.chronik.broadcastTx(setupTx.ser())).txid; } generate() { this.runner.send('generate'); } stop() { this.runner.send('stop'); } } exports.TestRunner = TestRunner; //# sourceMappingURL=testRunner.js.map