Closed mosesadeyinka closed 3 years ago
Exactly how the error states, your keys don't match.
Private | Address | Script |
---|---|---|
cQNr2RTfTjo4c5R9pyGm2CNRpRbJxKLpkm2JedbyC5dH9F33MKa4 | 2N4KUfKqvbBZzw1RjWs54mh8yk9v7vAcCyt | a91479774bc599362faa7deabfc342623e16d2d4320487 |
??? | 2N1uzcCxmVxRFVtwCtsJLVGbUZbSG3syZsE | a9145f160f26e702212ac2520000c89171caac3e8a2d87 |
??? | 2N3b4fXunecA3pgRGFwpdKUtzFFwFDifXp3 | a9147171cb5d4454a8335604230cb0e0e46f5200cfb387 |
You need to figure out what is what before it will work.
Also, none of those three addresses have any coins left. (2 had some coins but they've already been spent.)
Any idea or link on how to generate private key when creating an address, in Testnet or mainnet. I think that’s where the error is coming from.
Let privatekey = keyPair.toWif();
this is how I got the private key
It might be better to make a simple wrapper class that lets you keep everything together in one place.
```js // Be sure to install using: // npm install isomorphic-fetch // Requiring this adds fetch to global scope require('isomorphic-fetch'); const crypto = require('crypto'); const { ECPair, networks, payments, Psbt } = require('bitcoinjs-lib'); const WALLET_TYPES = ['p2wpkh', 'p2sh-p2wpkh']; class Wallet { constructor(privateKey, type, network = networks.bitcoin) { this.privateKey = privateKey; this.type = type; this.network = network; if (!Buffer.isBuffer(this.privateKey) || this.privateKey.length !== 32) { throw new Error(`privateKey must be 32 byte Buffer`); } if (!WALLET_TYPES.includes(type)) { throw new Error( `Wrong type ${type}, type must be in [${WALLET_TYPES.join(', ')}]`, ); } // The base URL for the blockstream API this.apiUrl = `https://blockstream.info/${ this.network.bech32 === 'bc' ? '' : 'testnet/' }api`; // null means we have never checked the utxos with the API this.utxos = null; this.keyPair = ECPair.fromPrivateKey(this.privateKey, { network: this.network, }); switch (this.type) { case 'p2wpkh': { this.payment = payments.p2wpkh({ pubkey: this.keyPair.publicKey, network: this.network, }); break; } case 'p2sh-p2wpkh': { this.payment = payments.p2sh({ redeem: payments.p2wpkh({ pubkey: this.keyPair.publicKey, network: this.network, }), network: this.network, }); break; } default: { throw new Error('no type'); } } } static import(importData) { const { wif, type, network } = importData; const pair = ECPair.fromWIF(wif, network); return new Wallet(pair.privateKey, type, network); } static makeRandom(type = 'p2sh-p2wpkh', network = networks.bitcoin) { return new Wallet(crypto.randomBytes(32), type, network); } export() { return { wif: this.keyPair.toWIF(), type: this.type, network: this.network, }; } get script() { return this.payment.output; } get address() { return this.payment.address; } get redeemScript() { if (this.payment.redeem) { return this.payment.redeem.output; } } async getUtxos() { const response = await fetch(`${this.apiUrl}/address/${this.address}/utxo`); if (!response.ok) { throw new Error(await response.text()); } const data = await response.json(); // Sort by descending value this.utxos = data.sort((a, b) => b.value - a.value); return this.utxos; } async broadcast(txHex) { const response = await fetch(`${this.apiUrl}/tx`, { method: 'POST', body: txHex, }); if (!response.ok) { throw new Error(await response.text()); } // returns the txid return response.text(); } calculateBalanceFromUtxos() { return (this.utxos || []).reduce((total, utxo) => total + utxo.value, 0); } async makePsbt(toAddress, amount) { const fee = 1000; // For mainnet this should be dynamically calculated const smallestOutputSize = 564; // "dust limit" if (amount < smallestOutputSize) { throw new Error('send amount too small'); } if (this.utxos === null) { await this.getUtxos(); } if (this.calculateBalanceFromUtxos() < amount + fee) { throw new Error(`Not enough coins`); } const shuffleArray = array => { for (let i = array.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } }; let totalInputAmount = 0; const inputs = []; // Add inputs for (const utxo of this.utxos) { if (totalInputAmount >= amount + fee) { break; } inputs.push({ hash: utxo.txid, index: utxo.vout, witnessUtxo: { script: this.script, value: utxo.value, }, ...(this.redeemScript ? { redeemScript: this.redeemScript } : {}), }); totalInputAmount += utxo.value; } // Add outputs const outputs = []; outputs.push({ address: toAddress, value: amount, }); if (totalInputAmount - amount - fee >= smallestOutputSize) { // Add change outputs.push({ address: this.address, value: totalInputAmount - amount - fee, }); } // randomize the order of inputs and outputs shuffleArray(inputs); shuffleArray(outputs); // add to psbt const psbt = new Psbt({ network: this.network }); psbt.addInputs(inputs); psbt.addOutputs(outputs); return psbt; } async sendCoins(toAddress, amount) { const psbt = await this.makePsbt(toAddress, amount); // sign all inputs psbt.signAllInputs(this); psbt.finalizeAllInputs(); // returns the txid return this.broadcast(psbt.extractTransaction().toHex()); } // Implement Signer interface get publicKey() { return this.keyPair.publicKey; } getPublicKey() { return this.keyPair.publicKey; } sign(hash, lowR) { return this.keyPair.sign(hash, lowR); } } ```
This can be used like this: ("p2wpkh" is pure Segwit, "p2sh-p2wpkh" is Segwit wrapped in a more-compatible address format) ("p2wpkh" is cheaper fees to spend since it uses less space)
```js const wallet = Wallet.makeRandom('p2sh-p2wpkh', networks.testnet); console.log(wallet.address); // outputs the address console.log(wallet.export()); // This outputs a data object like this // { wif: '...', type: 'p2sh-p2wpkh', network: { ... } } // now you can use the wallet class to create your script and redeemScript for you psbt.addInput({ hash: '...', index: 0, sequence: 0xffffffff, witnessUtxo: { script: wallet.script, value: 90000, }, redeemScript: wallet.redeemScript, // wallet.redeemScript will be undefined if the wallet is "p2wpkh" // But that is ok, since p2wpkh doesn't need a redeemScript }) // You can also pass this wallet directly in as a signer for PSBT, because it implements the Signer interface. psbt.signInput(0, wallet); // You can also use async/await to get info from an API and send the coins // Copying the following into a script js file and running it with 2 args: // == send 1234 satoshis to ADDRESS // node script.js ADDRESS 1234 // == or generate a new wallet to paste into your script == // node script.js gen async function main(args) { if (args[0] === 'gen') { // generate new wallet if first argument is 'gen' console.log(Wallet.makeRandom('p2wpkh', networks.testnet).export()); return; } const [toAddress, amount] = args; const wallet = Wallet.import({ wif: 'cTByh4w8RVc9PtZJL9X8HvS3tXPohvUM9w1DJ914bD2ECp3xdnNZ', type: 'p2wpkh', network: { messagePrefix: '\u0018Bitcoin Signed Message:\n', bech32: 'tb', bip32: { public: 70617039, private: 70615956 }, pubKeyHash: 111, scriptHash: 196, wif: 239, }, }); await wallet.getUtxos(); if (wallet.utxos.length === 0) { throw new Error(`Need coins. Please send to ${wallet.address}`); } console.log( `Current balance: ${wallet.calculateBalanceFromUtxos() / 1e8} tBTC`, ); console.log(`Current utxos:`); console.log(wallet.utxos); if (toAddress !== undefined && !isNaN(parseInt(amount))) { const value = parseInt(amount); const txid = await wallet.sendCoins(toAddress, value); console.log(`Sent ${value / 1e8} tBTC coins at txid ${txid}`); } } main(process.argv.slice(2)).catch(console.error); ```
(FYI: You can click "Wallet class" and "Usage" to open the code examples in the comment above)
the wallet class
// The testnet code to sign a transaction for broadcast
const TESTNET = bitcoin.networks.testnet;
const psbt = new bitcoin.Psbt({ network: TESTNET });
psbt.addInput({
hash: '266c68e8c85349956cce274159bfbad46ab913f4484ee733a6b9b48632efaad1',
index: 0,
sequence: 0xffffffff,
witnessUtxo: {
script: wallet.script,
value: 90000,
},
redeemScript: wallet.redeemScript, // wallet.redeemScript will be undefined if the wallet is "p2wpkh"
// But that is ok, since p2wpkh doesn't need a redeemScript
});
psbt.addOutput({
address: 'mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB',
value: 80000,
network: TESTNET,
});
psbt.signInput(0, wallet);
psbt.validateSignaturesOfInput(0);
psbt.finalizeAllInputs();
const rawTx = psbt.extractTransaction().toHex();
console.log(rawTx);
here is the signed result
rawTx log :
02000000000101d1aaef3286b4b9a633e74e48f413b96ad4babf594127ce6c954953c8e8686c260000 000017160014776589c83b9ca11d8574f9e13f32829a65f11d63ffffffff0180380100000000001976a9149f9a7 abd600c0caa03983a77c8c3df8e062cb2fa88ac0247304402200fecb4476cb135c8d868fc99209f533cdc3f3427 dc4b3519a64f88bb5094eedc02206923581163169a774541921365808795c7446be7ecd0a9a1d88396257b8c9fc 401210245816b5b24fa965da1982c9f65c42b130a3c4b47288e47a2c79141ce199b8d0700000000
The rawTx when i try to broadcast to testnet network gives error "sendrawtransaction RPC error: {"code":-25,"message":"bad-txns-inputs-missingorspent"}"
I looked at the transaction here: https://blockstream.info/testnet/tx/266c68e8c85349956cce274159bfbad46ab913f4484ee733a6b9b48632efaad1?expand
Perhaps you want to use index: 1,
and value: 1969075,
which is the value to the 2nd (index 1) output.
The keys must be the same though.
I have updated the Wallet class and Usage comment above with some useful features using blockstream API.
Be careful when doing things quickly, since blockstream API is replicated across many load balancers, so if you run the get utxos query right after sending a tx, it will return different results every time for a few seconds.
Please use the example above to help in your understanding.
Note: redeemScript can not be defined as undefined. If you do so, the library throws an error, so I made it only add redeemScript if it exists.
I modified some codes in the wallet class that was throwing an error
The execution:
let res = await wallet.sendCoins("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB", "1909075","2N4K9pEkDWWrsfYMx5jmVUrPg7zJXj4xaWm"); console.log(res);
the broadcast throw error "mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)"
Your modifications won't work.
If you want to use your own key, use my code as is, and import the WIF private key along with the key type and network.
sendcoin()
makeRandom(type = 'p2sh-p2wpkh', network = networks.bitcoin) {
return new Wallet(crypto.randomBytes(32), type, network);
}
generates new private keys on every call which isn't stored crypto.randomBytes(32)
let's move here
this.address
is the random address that is just been generated with random private key which is empty
const response = await fetch(
${this.apiUrl}/address/${this.address}/utxo);
This fetch is for new address that was generated?
What of the address that contains the money?
so i modified the code to accept the parameter (address)of which contains the money & get the txtid
thanks, pls help out.
i solved the code & broadcast the BTC by using a unique private key, i will upload the finish codes for others
you don't have to use makeRandom every time.
you can use import.
If you want to see what to pass import, look at the Usage example I added.
For example:
const wallet = Wallet.import({
wif: 'cQNr2RTfTjo4c5R9pyGm2CNRpRbJxKLpkm2JedbyC5dH9F33MKa4',
type: 'p2sh-p2wpkh',
// This is testnet network info
network: {
messagePrefix: '\u0018Bitcoin Signed Message:\n',
bech32: 'tb',
bip32: { public: 70617039, private: 70615956 },
pubKeyHash: 111,
scriptHash: 196,
wif: 239,
},
});
await wallet.getUtxos();
if (wallet.utxos.length === 0) {
throw new Error(`Need coins. Please send to ${wallet.address}`);
}
console.log(
`Current balance: ${wallet.calculateBalanceFromUtxos() / 1e8} tBTC`,
);
console.log(`Current utxos:`);
console.log(wallet.utxos);
const txid = await wallet.sendCoins(toAddress, value);
console.log(`Sent ${value / 1e8} tBTC coins at txid ${txid}`);
@junderw can you tell me the difference b/w p2sh-p2wpkh & p2wpkh? people using non segwit addresses won't be able to send me payments if i use p2wpkh address Right?
Hello i created a testnet address.
here is my code
but i keep getting this error. Error: Can not sign for this input with the key