Skip to content

Commit

Permalink
sendraw: Added command to send raw transaction
Browse files Browse the repository at this point in the history
This allows sending batched transactions
  • Loading branch information
Nathanwoodburn committed Jun 14, 2023
1 parent 7e80fd7 commit db01729
Showing 1 changed file with 99 additions and 11 deletions.
110 changes: 99 additions & 11 deletions bin/hsd-ledger
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
*/

const Config = require('bcfg');
const {NodeClient, WalletClient} = require('hs-client');
const {hd, MTX, Network} = require('hsd');
const {util, USB, LedgerHSD, LedgerChange} = require('..');
const {Device} = USB;
const { NodeClient, WalletClient } = require('hs-client');
const { hd, MTX, Network } = require('hsd');
const { Rules } = require('hsd/lib/covenants');
const { hashName, types } = Rules;
const { util, HID, LedgerHSD, LedgerChange, LedgerCovenant } = require('..');
const { Device } = HID;

/**
* Global constants
Expand All @@ -24,7 +26,8 @@ const VALID_CMDS = [
'getwallets',
'getaccounts',
'getaccount',
'getbalance'
'getbalance',
'sendraw'
];

const VERSION = require('../package').version;
Expand Down Expand Up @@ -74,7 +77,7 @@ async function createAddress(client, config, ledger, args) {

console.log(`Verify address on Ledger device: ${addr.address}`);

await ledger.getAddress(account, addr.branch, addr.index, {confirm: true});
await ledger.getAddress(account, addr.branch, addr.index, { confirm: true });
}

async function sendToAddress(wclient, nclient, config, ledger, args) {
Expand Down Expand Up @@ -106,7 +109,7 @@ async function sendToAddress(wclient, nclient, config, ledger, args) {
if (!key || !key.branch)
throw new Error('Expected change address.');

const {account, branch, index} = key;
const { account, branch, index } = key;
const coinType = network.keyPrefix.coinType;
const options = {
change: new LedgerChange({
Expand All @@ -123,6 +126,88 @@ async function sendToAddress(wclient, nclient, config, ledger, args) {
const txid = await nclient.execute('sendrawtransaction', [rawtx]);

console.log(`Submitted TXID: ${txid}`);
}
async function sendRaw(wclient, nclient, config, ledger, args) { // Create a function to sign raw transactions
if (args.length !== 2) // Make sure there are two arguments (batch, names)
throw new Error('Invalid arguments'); // Throw an error if there are not two arguments

const network = Network.get(config.str('network')); // Get the network
const id = config.str('wallet-id'); // Get the wallet id
const acct = config.str('account-name'); // Get the account name
// Log the arguments to the console (for debugging)

const batch = JSON.parse(args[0]); // Get the batch
const names = JSON.parse(args[1]); // Get the names
await wclient.execute('selectwallet', [id]); // Select the wallet

try {
const mtx = MTX.fromJSON(batch.result); // Create a new MTX from the JSON
const hashes = {}; // Create an empty object to store the hashes
for (const name of names) { // Loop through the names
const hash = hashName(name); // Hash the name
hashes[hash] = name; // Add the hash to the hashes object to use later
}


let i, key; // Create variables to use later
const options = []; // Create an empty array to store the options
for (i = mtx.outputs.length - 1; i >= 0; i--) { // Loop through the outputs
const output = mtx.outputs[i]; // Get the output
const addr = output.address.toString(network.type); // Get the address
key = await wclient.getKey(id, addr); // Get the key
if (!key) // If there is no key
continue; // Continue to the next output
if (key.branch === 1) { // If the key is a change address
if (options.change) // If there is already a change address
throw new Error('Transaction should only have one change output.'); // Throw an error
const path = `m/44'/${network.keyPrefix.coinType}'/${key.account}'/${key.branch}/${key.index}`; // Create the derivation path
options.change = new LedgerChange({ path: `m/44'/${network.keyPrefix.coinType}'/${key.account}'/${key.branch}/${key.index}`, index: i, version: 0 }); // Add the change address to the options
}
const { account, branch, index } = key; // Get the account, branch, and index from the key
const coinType = network.keyPrefix.coinType; // Get the coin type from the network
switch (output.covenant.type) {
case types.NONE:
case types.OPEN:
case types.BID:
case types.FINALIZE:
break;
case types.REVEAL:
case types.REDEEM:
case types.REGISTER:
case types.UPDATE:
case types.RENEW:
case types.TRANSFER:
case types.REVOKE: { // If the covenant type is any of REVEAL, REDEEM, REGISTER, UPDATE, RENEW, TRANSFER, or REVOKE
if (options.covenants == null) // If there are no covenants
options.covenants = []; // Create an empty array for the covenants
const hash = output.covenant.items[0]; // Get the hash from the covenant
const name = hashes[hash]; // Get the name from the hashes object (is needed for SPV nodes)
if (name == undefined) { // If the name is not found
console.log("Name not found in file"); // Log that the name was not found
console.log(hash); // Log the hash (for debugging)
}
options.covenants.push(new LedgerCovenant({ index: i, name })); // Add the covenant to the options

break;
}
default:
throw new Error('Unrecognized covenant type.');

}

} // end for loop
util.displayDetails(console, network, mtx, options); // Display the details to the log for user verification
const signed = await ledger.signTransaction(mtx, options); // Sign the transaction with the ledger
const rawtx = signed.encode().toString('hex'); // Encode the transaction as hex
const txid = await nclient.execute('sendrawtransaction', [rawtx]); // Send the transaction to the network
console.log(`Submitted TXID: ${txid}`); // Log the TXID to the console to view the transaction on a block explorer

} catch (err) { // Catch any errors
console.error(err); // Log the error to the console
}



}

async function getWallets(client, args) {
Expand Down Expand Up @@ -210,12 +295,12 @@ async function main() {
const id = config.str('wallet-id');
const token = config.str('token');

if(config.str('help') && argv.length === 0) {
if (config.str('help') && argv.length === 0) {
usage();
process.exit(0);
}

if(config.str('version') && argv.length === 0) {
if (config.str('version') && argv.length === 0) {
version();
process.exit(0);
}
Expand Down Expand Up @@ -290,13 +375,16 @@ async function main() {
await getBalance(wclient, config, args);
break;

case VALID_CMDS[8]:
await sendRaw(wclient, nclient, config, ledger, args);
break;
default:
usage(new Error('Must provide valid command.'));
process.exit(1);
break;
}
} catch(e) {
throw(e);
} catch (e) {
throw (e);
} finally {
await wclient.close();
await nclient.close();
Expand Down

0 comments on commit db01729

Please sign in to comment.