209 lines
7.5 KiB
JavaScript
209 lines
7.5 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
const assert = require('bsert');
|
||
|
const Network = require('hsd/lib/protocol/network');
|
||
|
const FullNode = require('hsd/lib/node/fullnode');
|
||
|
const MTX = require('hsd/lib/primitives/mtx');
|
||
|
const Address = require('hsd/lib/primitives/address');
|
||
|
const Output = require('hsd/lib/primitives/output');
|
||
|
const Script = require('hsd/lib/script/script');
|
||
|
const rules = require('hsd/lib/covenants/rules');
|
||
|
const {types} = rules;
|
||
|
const {Resource} = require('hsd/lib/dns/resource');
|
||
|
// const WalletClient = require('hsd/lib/client/wallet');
|
||
|
const { NodeClient, WalletClient } = require('hs-client');
|
||
|
|
||
|
const network = Network.get('main');
|
||
|
const minimist = require('minimist');
|
||
|
const args = minimist(process.argv.slice(2));
|
||
|
|
||
|
|
||
|
//! TODO this needs to hook into existing node
|
||
|
// const node = new FullNode({
|
||
|
// memory: true,
|
||
|
// network: 'main'
|
||
|
// });
|
||
|
|
||
|
const nclient = new NodeClient({
|
||
|
url: args.hsdIP || 'localhost',
|
||
|
apiKey: args.apiKey || '',
|
||
|
ssl: false,
|
||
|
host: args.hsdIP || 'localhost',
|
||
|
port: 12037
|
||
|
});
|
||
|
|
||
|
const wclient = new WalletClient({
|
||
|
url: args.hsdIP || 'localhost',
|
||
|
apiKey: args.apiKey || '',
|
||
|
ssl: false,
|
||
|
host: args.hsdIP || 'localhost',
|
||
|
port: 12039,
|
||
|
});
|
||
|
|
||
|
nclient.open();
|
||
|
wclient.open();
|
||
|
|
||
|
|
||
|
// const {wdb} = wclient.require('walletdb');
|
||
|
|
||
|
const price = args.price/1000000 || 1.234567; // 1.234567 HNS
|
||
|
const domainAddress = args.domainAddress || 'hs1qsyy6eujpt2wv0caa0d26tvwejx77zdks2pj8nz';
|
||
|
const buyerAddress = args.buyer || 'hs1qcq8nzmlus8z5qqn6wvls9r0vg98tr6d6ga3fau';
|
||
|
const sellerAddress = args.seller || 'hs1q962sf24659wm6ev26gshrfwg8ruhve4c34f0ur';
|
||
|
const ns = args.nameInfo || {"name": "woodburn1","nameHash": "c93640a7772f786391c1c3ff2e99e4aaa442b6f2cf1fc6ec1790f2ea818dec08","state": "CLOSED","height": 175454,"renewal": 252162,"owner": {"hash": "337edb6e00aaf602a218d35d499b088872d2e74b9463066da845edccbe797c27","index": 4},"value": 1000000,"highest": 1000000,"data": "0001036e733108776f6f646275726e0001036e7332c006060204546573740548656c6c6f","transfer": 0,"revoked": 0,"claimed": 0,"renewals": 4,"registered": true,"expired": false,"weak": false,"stats": {"renewalPeriodStart": 252162,"renewalPeriodEnd": 357282,"blocksUntilExpire": 95192,"daysUntilExpire": 661.06}};
|
||
|
const walletId = args.walletId || 'hot';
|
||
|
|
||
|
const name = ns.name;
|
||
|
const nameHash = ns.nameHash;
|
||
|
|
||
|
|
||
|
|
||
|
// Alice constructs an incomplete transaction.
|
||
|
// input 0 and output 1 are committed by Alice's SINGLEREVERSE signature.
|
||
|
// output 0 can be added by either party since it's construction is
|
||
|
// dictated completely by consensus rules (it isn't signed yet).
|
||
|
//
|
||
|
// input 0: TRANSFER UTXO --> output 0: FINALIZE covenant
|
||
|
// (null) --- output 1: payment to Alice
|
||
|
|
||
|
const owner = ns.owner;
|
||
|
wclient.execute('selectwallet', [walletId]);
|
||
|
const coin = wclient.getCoin(owner.hash, owner.index);
|
||
|
|
||
|
const output0 = new Output();
|
||
|
output0.value = coin.value;
|
||
|
output0.address = buyerAddress;
|
||
|
output0.covenant.setFinalize(
|
||
|
nameHash,
|
||
|
ns.height,
|
||
|
Buffer.from(name, 'ascii'),
|
||
|
0, // flags, may be required if name was CLAIMed
|
||
|
ns.claimed,
|
||
|
ns.renewals,
|
||
|
wdb.getRenewalBlock()
|
||
|
);
|
||
|
|
||
|
const output1 = new Output();
|
||
|
output1.address = sellerAddress;
|
||
|
output1.value = price;
|
||
|
|
||
|
const mtx = new MTX();
|
||
|
mtx.addCoin(coin);
|
||
|
mtx.outputs.push(output0);
|
||
|
mtx.outputs.push(output1);
|
||
|
|
||
|
// Sign
|
||
|
const rings = wclient.deriveInputs(mtx);
|
||
|
assert(rings.length === 1);
|
||
|
const signed = mtx.sign(
|
||
|
rings,
|
||
|
Script.hashType.SINGLEREVERSE | Script.hashType.ANYONECANPAY
|
||
|
);
|
||
|
assert(signed === 1);
|
||
|
|
||
|
assert(mtx.verify());
|
||
|
blob = mtx.encode().toString('hex');
|
||
|
console.log(blob);
|
||
|
|
||
|
|
||
|
// COMPLETE TX
|
||
|
|
||
|
// it('should complete transaction', async () => {
|
||
|
// // Bob receives the hex string as a blob and decodes.
|
||
|
// const mtx = MTX.decode(Buffer.from(blob, 'hex'));
|
||
|
|
||
|
// // Bob should verify all the data in the MTX to ensure everything is valid,
|
||
|
// // but this is the minimum.
|
||
|
// const input0 = mtx.input(0).clone(); // copy input with Alice's signature
|
||
|
// const coinEntry = await nclient.chain.db.readCoin(input0.prevout);
|
||
|
// assert(coinEntry); // ensures that coin exists and is still unspent
|
||
|
|
||
|
// const coin = coinEntry.toCoin(input0.prevout);
|
||
|
// assert(coin.covenant.type === types.TRANSFER);
|
||
|
// const addr = new Address({
|
||
|
// version: coin.covenant.items[2].readInt8(),
|
||
|
// hash: coin.covenant.items[3]
|
||
|
// });
|
||
|
// assert.deepStrictEqual(addr, bobReceive); // transfer is to Bob's address
|
||
|
|
||
|
// // Fund the TX.
|
||
|
// // The hsd wallet is not designed to handle partially-signed TXs
|
||
|
// // or coins from outside the wallet, so a little hacking is needed.
|
||
|
// const changeAddress = await bob.changeAddress();
|
||
|
// const rate = await wdb.estimateFee();
|
||
|
// const coins = await bob.getSmartCoins();
|
||
|
// // Add the external coin to the coin selector so we don't fail assertions
|
||
|
// coins.push(coin);
|
||
|
// await mtx.fund(coins, {changeAddress, rate});
|
||
|
// // The funding mechanism starts by wiping out existing inputs
|
||
|
// // which for us includes Alice's signature. Replace it from our backup.
|
||
|
// mtx.inputs[0].inject(input0);
|
||
|
|
||
|
// // Rearrange outputs.
|
||
|
// // Since we added a change output, the SINGELREVERSE is now broken:
|
||
|
// //
|
||
|
// // input 0: TRANSFER UTXO --> output 0: FINALIZE covenant
|
||
|
// // input 1: Bob's funds --- output 1: payment to Alice
|
||
|
// // (null) --- output 2: change to Bob
|
||
|
// const outputs = mtx.outputs.slice();
|
||
|
// mtx.outputs = [outputs[0], outputs[2], outputs[1]];
|
||
|
|
||
|
// // Prepare to wait for mempool acceptance (race condition)
|
||
|
// const waiter = new Promise((resolve, reject) => {
|
||
|
// node.mempool.once('tx', resolve);
|
||
|
// });
|
||
|
|
||
|
// // Sign & Broadcast
|
||
|
// // Bob uses SIGHASHALL. The final TX looks like this:
|
||
|
// //
|
||
|
// // input 0: TRANSFER UTXO --> output 0: FINALIZE covenant
|
||
|
// // input 1: Bob's funds --- output 1: change to Bob
|
||
|
// // (null) --- output 2: payment to Alice
|
||
|
// const tx = await bob.sendMTX(mtx);
|
||
|
// bobFee = tx.getFee(mtx.view);
|
||
|
// assert(tx.verify(mtx.view));
|
||
|
|
||
|
// // Wait for mempool and check
|
||
|
// await waiter;
|
||
|
// assert(node.mempool.hasEntry(tx.hash()));
|
||
|
|
||
|
// // Confirm
|
||
|
// await mineBlocks(1);
|
||
|
// });
|
||
|
|
||
|
// it('should verify that name has been swapped', async () => {
|
||
|
// const aliceNewBalance = await alice.getBalance();
|
||
|
// const bobNewBalance = await bob.getBalance();
|
||
|
|
||
|
// // Alice got the monies
|
||
|
// // Note: This test works right now because the value of the name
|
||
|
// // Alice won in the auction is ZERO (she had the only bid)
|
||
|
// // See https://github.com/handshake-org/hsd/pull/464 for explanation
|
||
|
// // Currently hsd wallet does not account for FINALIZE correctly
|
||
|
// assert.strictEqual(
|
||
|
// aliceNewBalance.confirmed,
|
||
|
// aliceOriginalBalance.confirmed + price
|
||
|
// );
|
||
|
// assert.strictEqual(
|
||
|
// bobNewBalance.confirmed,
|
||
|
// bobOriginalBalance.confirmed - price - bobFee
|
||
|
// );
|
||
|
|
||
|
// // Bob got the name
|
||
|
// const ns = await node.getNameStatus(nameHash);
|
||
|
// const owner = ns.owner;
|
||
|
// let coin = await alice.getCoin(owner.hash, owner.index);
|
||
|
// assert(!coin);
|
||
|
// coin = await bob.getCoin(owner.hash, owner.index);
|
||
|
// assert(coin);
|
||
|
|
||
|
// const resource = Resource.fromJSON({
|
||
|
// records: [{type: 'TXT', txt: ['Thanks Alice! --Bob']}]
|
||
|
// });
|
||
|
// await bob.sendUpdate(name, resource);
|
||
|
// await mineBlocks(network.names.treeInterval);
|
||
|
// const actual = await node.chain.db.getNameState(nameHash);
|
||
|
// assert.bufferEqual(resource.encode(), actual.data);
|
||
|
|
||
|
// // The End
|
||
|
// });
|