Browse Source

Merge branch 'segwit' of git://github.com/braydonf/btcjs into braydonf-segwit

patch-2
Justin Langston 7 years ago
parent
commit
5dbd275677
No known key found for this signature in database GPG Key ID: EBB3714C72F9FE5D
  1. 10
      lib/address.js
  2. 144
      lib/script/interpreter.js
  3. 63
      lib/script/script.js
  4. 1
      lib/transaction/index.js
  5. 23
      lib/transaction/input/input.js
  6. 103
      lib/transaction/input/multisigscripthash.js
  7. 149
      lib/transaction/sighashwitness.js
  8. 132
      lib/transaction/transaction.js
  9. 5
      test/address.js
  10. 3
      test/data/bitcoind/tx_invalid.json
  11. 106
      test/script/interpreter.js
  12. 66
      test/script/script.js
  13. 33
      test/transaction/input/multisigscripthash.js
  14. 24
      test/transaction/sighashwitness.js
  15. 425
      test/transaction/transaction.js

10
lib/address.js

@ -259,11 +259,16 @@ Address._transformScript = function(script, network) {
* @param {Array} publicKeys - a set of public keys to create an address
* @param {number} threshold - the number of signatures needed to release the funds
* @param {String|Network} network - either a Network instance, 'livenet', or 'testnet'
* @param {boolean=} nestedWitness - if the address uses a nested p2sh witness
* @return {Address}
*/
Address.createMultisig = function(publicKeys, threshold, network) {
Address.createMultisig = function(publicKeys, threshold, network, nestedWitness) {
network = network || publicKeys[0].network || Networks.defaultNetwork;
return Address.payingTo(Script.buildMultisigOut(publicKeys, threshold), network);
var redeemScript = Script.buildMultisigOut(publicKeys, threshold);
if (nestedWitness) {
return Address.payingTo(Script.buildWitnessMultisigOutFromScript(redeemScript), network);
}
return Address.payingTo(redeemScript, network);
};
/**
@ -336,7 +341,6 @@ Address.fromScriptHash = function(hash, network) {
Address.payingTo = function(script, network) {
$.checkArgument(script, 'script is required');
$.checkArgument(script instanceof Script, 'script must be instance of Script');
return Address.fromScriptHash(Hash.sha256ripemd160(script.toBuffer()), network);
};

144
lib/script/interpreter.js

@ -31,6 +31,80 @@ var Interpreter = function Interpreter(obj) {
}
};
Interpreter.prototype.verifyWitnessProgram = function(version, program, witness, satoshis, flags) {
var scriptPubKey = new Script();
var stack = [];
if (version === 0) {
if (program.length === 32) {
if (witness.length === 0) {
this.errstr = 'SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY';
return false;
}
var scriptPubKeyBuffer = witness[witness.length - 1];
scriptPubKey = new Script(scriptPubKeyBuffer);
var hash = Hash.sha256(scriptPubKeyBuffer);
if (hash.toString('hex') !== program.toString('hex')) {
this.errstr = 'SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH';
return false;
}
stack = witness.slice(0, -1);
} else if (program.length === 20) {
if (witness.length !== 2) {
this.errstr = 'SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH';
return false;
}
scriptPubKey.add(Opcode.OP_DUP);
scriptPubKey.add(Opcode.OP_HASH160);
scriptPubKey.add(program);
scriptPubKey.add(Opcode.OP_EQUALVERIFY);
scriptPubKey.add(Opcode.OP_CHECKSIG);
stack = witness;
} else {
this.errstr = 'SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH';
return false;
}
} else if ((flags & Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)) {
this.errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM';
return false;
} else {
return true;
}
this.initialize();
this.set({
script: scriptPubKey,
stack: stack,
sigversion: 1,
satoshis: satoshis
});
if (!this.evaluate()) {
return false;
}
if (this.stack.length !== 1) {
this.errstr = 'SCRIPT_ERR_EVAL_FALSE';
return false;
}
var buf = this.stack[this.stack.length - 1];
if (!Interpreter.castToBool(buf)) {
this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_STACK';
return false;
}
return true;
};
/**
* Verifies a Script by executing it and returns true if it is valid.
* This function needs to be provided with the scriptSig and the scriptPubkey
@ -41,10 +115,13 @@ var Interpreter = function Interpreter(obj) {
* to check signature validity for some opcodes like OP_CHECKSIG)
* @param {number} nin - index of the transaction input containing the scriptSig verified.
* @param {number} flags - evaluation flags. See Interpreter.SCRIPT_* constants
* @param {number} witness - array of witness data
* @param {number} satoshis - number of satoshis created by this output
*
* Translated from bitcoind's VerifyScript
*/
Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags) {
Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags, witness, satoshis) {
var Transaction = require('../transaction');
if (_.isUndefined(tx)) {
tx = new Transaction();
@ -55,10 +132,19 @@ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags)
if (_.isUndefined(flags)) {
flags = 0;
}
if (_.isUndefined(witness)) {
witness = null;
}
if (_.isUndefined(satoshis)) {
satoshis = 0;
}
this.set({
script: scriptSig,
tx: tx,
nin: nin,
sigversion: 0,
satoshis: 0,
flags: flags
});
var stackCopy;
@ -103,6 +189,22 @@ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags)
return false;
}
var hadWitness = false;
if ((flags & Interpreter.SCRIPT_VERIFY_WITNESS)) {
var witnessValues = {};
if (scriptPubkey.isWitnessProgram(witnessValues)) {
hadWitness = true;
if (scriptSig.toBuffer().length !== 0) {
return false;
}
if (!this.verifyWitnessProgram(witnessValues.version, witnessValues.program, witness, satoshis, flags)) {
return false;
}
}
}
// Additional validation for spend-to-script-hash transactions:
if ((flags & Interpreter.SCRIPT_VERIFY_P2SH) && scriptPubkey.isScriptHashOut()) {
// scriptSig must be literals-only or validation fails
@ -144,8 +246,31 @@ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags)
if (!Interpreter.castToBool(stackCopy[stackCopy.length - 1])) {
this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_P2SH_STACK';
return false;
} else {
return true;
}
if ((flags & Interpreter.SCRIPT_VERIFY_WITNESS)) {
var p2shWitnessValues = {};
if (redeemScript.isWitnessProgram(p2shWitnessValues)) {
hadWitness = true;
var redeemScriptPush = new Script();
redeemScriptPush.add(redeemScript.toBuffer());
if (scriptSig.toHex() !== redeemScriptPush.toHex()) {
this.errstr = 'SCRIPT_ERR_WITNESS_MALLEATED_P2SH';
return false;
}
if (!this.verifyWitnessProgram(p2shWitnessValues.version, p2shWitnessValues.program, witness, satoshis, flags)) {
return false;
}
stack = [stack[0]];
}
}
if ((flags & Interpreter.SCRIPT_VERIFY_WITNESS)) {
if (!hadWitness && witness.length > 0) {
this.errstr = 'SCRIPT_ERR_WITNESS_UNEXPECTED';
return false;
}
}
}
@ -158,6 +283,8 @@ Interpreter.prototype.initialize = function(obj) {
this.stack = [];
this.altstack = [];
this.pc = 0;
this.satoshis = 0;
this.sigversion = 0;
this.pbegincodehash = 0;
this.nOpCount = 0;
this.vfExec = [];
@ -173,6 +300,8 @@ Interpreter.prototype.set = function(obj) {
this.altstack = obj.altack || this.altstack;
this.pc = typeof obj.pc !== 'undefined' ? obj.pc : this.pc;
this.pbegincodehash = typeof obj.pbegincodehash !== 'undefined' ? obj.pbegincodehash : this.pbegincodehash;
this.sigversion = typeof obj.sigversion !== 'undefined' ? obj.sigversion : this.sigversion;
this.satoshis = typeof obj.satoshis !== 'undefined' ? obj.satoshis : this.satoshis;
this.nOpCount = typeof obj.nOpCount !== 'undefined' ? obj.nOpCount : this.nOpCount;
this.vfExec = obj.vfExec || this.vfExec;
this.errstr = obj.errstr || this.errstr;
@ -191,6 +320,9 @@ Interpreter.LOCKTIME_THRESHOLD_BN = new BN(Interpreter.LOCKTIME_THRESHOLD);
// bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104
Interpreter.SCRIPT_VERIFY_NONE = 0;
// Making v1-v16 witness program non-standard
Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM = (1 << 12);
// Evaluate P2SH subscripts (softfork safe, BIP16).
Interpreter.SCRIPT_VERIFY_P2SH = (1 << 0);
@ -231,6 +363,8 @@ Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7);
// CLTV See BIP65 for details.
Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9);
Interpreter.SCRIPT_VERIFY_WITNESS = (1 << 10);
Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 11);
Interpreter.castToBool = function(buf) {
for (var i = 0; i < buf.length; i++) {
@ -1111,7 +1245,7 @@ Interpreter.prototype.step = function() {
try {
sig = Signature.fromTxFormat(bufSig);
pubkey = PublicKey.fromBuffer(bufPubkey, false);
fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript);
fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript, this.sigversion, this.satoshis);
} catch (e) {
//invalid sig or pubkey
fSuccess = false;
@ -1200,7 +1334,7 @@ Interpreter.prototype.step = function() {
try {
sig = Signature.fromTxFormat(bufSig);
pubkey = PublicKey.fromBuffer(bufPubkey, false);
fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript);
fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript, this.sigversion, this.satoshis);
} catch (e) {
//invalid sig or pubkey
fOk = false;

63
lib/script/script.js

@ -404,6 +404,49 @@ Script.prototype.isScriptHashOut = function() {
buf[buf.length - 1] === Opcode.OP_EQUAL);
};
/**
* @returns {boolean} if this is a p2wsh output script
*/
Script.prototype.isWitnessScriptHashOut = function() {
var buf = this.toBuffer();
return (buf.length === 34 && buf[0] === 0 && buf[1] === 32);
};
/**
* @returns {boolean} if this is a p2wpkh output script
*/
Script.prototype.isWitnessPublicKeyHashOut = function() {
var buf = this.toBuffer();
return (buf.length === 22 && buf[0] === 0 && buf[1] === 20);
};
/**
* @param {Object=} values - The return values
* @param {Number} values.version - Set with the witness version
* @param {Buffer} values.program - Set with the witness program
* @returns {boolean} if this is a p2wpkh output script
*/
Script.prototype.isWitnessProgram = function(values) {
if (!values) {
values = {};
}
var buf = this.toBuffer();
if (buf.length < 4 || buf.length > 42) {
return false;
}
if (buf[0] !== Opcode.OP_0 && !(buf[0] >= Opcode.OP_1 && buf[0] <= Opcode.OP_16)) {
return false;
}
if (buf.length === buf[1] + 2) {
values.version = buf[0];
values.program = buf.slice(2, buf.length);
return true;
}
return false;
};
/**
* @returns {boolean} if this is a p2sh input script
* Note that these are frequently indistinguishable from pubkeyhashin
@ -689,6 +732,15 @@ Script.prototype._addBuffer = function(buf, prepend) {
return this;
};
Script.prototype.hasCodeseparators = function() {
for (var i = 0; i < this.chunks.length; i++) {
if (this.chunks[i].opcodenum === Opcode.OP_CODESEPARATOR) {
return true;
}
}
return false;
};
Script.prototype.removeCodeseparators = function() {
var chunks = [];
for (var i = 0; i < this.chunks.length; i++) {
@ -733,6 +785,17 @@ Script.buildMultisigOut = function(publicKeys, threshold, opts) {
return script;
};
Script.buildWitnessMultisigOutFromScript = function(script) {
if (script instanceof Script) {
var s = new Script();
s.add(Opcode.OP_0);
s.add(Hash.sha256(script.toBuffer()));
return s;
} else {
throw new TypeError('First argument is expected to be a p2sh script');
}
};
/**
* A new Multisig input script for the given public keys, requiring m of those public keys to spend
*

1
lib/transaction/index.js

@ -5,3 +5,4 @@ module.exports.Output = require('./output');
module.exports.UnspentOutput = require('./unspentoutput');
module.exports.Signature = require('./signature');
module.exports.Sighash = require('./sighash');
module.exports.SighashWitness = require('./sighashwitness');

23
lib/transaction/input/input.js

@ -58,6 +58,7 @@ Input.prototype._fromObject = function(params) {
} else {
prevTxId = params.prevTxId;
}
this.witnesses = [];
this.output = params.output ?
(params.output instanceof Output ? params.output : new Output(params.output)) : undefined;
this.prevTxId = prevTxId || params.txidbuf;
@ -153,6 +154,13 @@ Input.prototype.getSignatures = function() {
);
};
Input.prototype.getSatoshisBuffer = function() {
$.checkState(this.output instanceof Output);
$.checkState(this.output._satoshisBN);
return new BufferWriter().writeUInt64LEBN(this.output._satoshisBN).toBuffer();
};
Input.prototype.isFullySigned = function() {
throw new errors.AbstractMethodInvoked('Input#isFullySigned');
};
@ -169,6 +177,21 @@ Input.prototype.clearSignatures = function() {
throw new errors.AbstractMethodInvoked('Input#clearSignatures');
};
Input.prototype.hasWitnesses = function() {
if (this.witnesses && this.witnesses.length > 0) {
return true;
}
return false;
};
Input.prototype.getWitnesses = function() {
return this.witnesses;
};
Input.prototype.setWitnesses = function(witnesses) {
this.witnesses = witnesses;
};
Input.prototype.isValidSignature = function(transaction, signature) {
// FIXME: Refactor signature so this is not necessary
signature.signature.nhashtype = signature.sigtype;

103
lib/transaction/input/multisigscripthash.js

@ -1,5 +1,7 @@
'use strict';
/* jshint maxparams:5 */
var _ = require('lodash');
var inherits = require('inherits');
var Input = require('./input');
@ -9,23 +11,36 @@ var $ = require('../../util/preconditions');
var Script = require('../../script');
var Signature = require('../../crypto/signature');
var Sighash = require('../sighash');
var PublicKey = require('../../publickey');
var SighashWitness = require('../sighashwitness');
var BufferWriter = require('../../encoding/bufferwriter');
var BufferUtil = require('../../util/buffer');
var TransactionSignature = require('../signature');
/**
* @constructor
*/
function MultiSigScriptHashInput(input, pubkeys, threshold, signatures) {
function MultiSigScriptHashInput(input, pubkeys, threshold, signatures, nestedWitness) {
/* jshint maxstatements:20 */
Input.apply(this, arguments);
var self = this;
pubkeys = pubkeys || input.publicKeys;
threshold = threshold || input.threshold;
signatures = signatures || input.signatures;
this.nestedWitness = nestedWitness ? true : false;
this.publicKeys = _.sortBy(pubkeys, function(publicKey) { return publicKey.toString('hex'); });
this.redeemScript = Script.buildMultisigOut(this.publicKeys, threshold);
$.checkState(Script.buildScriptHashOut(this.redeemScript).equals(this.output.script),
if (this.nestedWitness) {
var nested = Script.buildWitnessMultisigOutFromScript(this.redeemScript);
$.checkState(Script.buildScriptHashOut(nested).equals(this.output.script),
'Provided public keys don\'t hash to the provided output (nested witness)');
var scriptSig = new Script();
scriptSig.add(nested.toBuffer());
this.setScript(scriptSig);
} else {
$.checkState(Script.buildScriptHashOut(this.redeemScript).equals(this.output.script),
'Provided public keys don\'t hash to the provided output');
}
this.publicKeyIndex = {};
_.each(this.publicKeys, function(publicKey, index) {
self.publicKeyIndex[publicKey.toString()] = index;
@ -62,6 +77,31 @@ MultiSigScriptHashInput.prototype._serializeSignatures = function() {
});
};
MultiSigScriptHashInput.prototype.getScriptCode = function() {
var writer = new BufferWriter();
if (!this.redeemScript.hasCodeseparators()) {
var redeemScriptBuffer = this.redeemScript.toBuffer();
writer.writeVarintNum(redeemScriptBuffer.length);
writer.write(redeemScriptBuffer);
} else {
throw new Error('@TODO');
}
return writer.toBuffer();
};
MultiSigScriptHashInput.prototype.getSighash = function(transaction, privateKey, index, sigtype) {
var self = this;
var hash;
if (self.nestedWitness) {
var scriptCode = self.getScriptCode();
var satoshisBuffer = self.getSatoshisBuffer();
hash = SighashWitness.sighash(transaction, sigtype, index, scriptCode, satoshisBuffer);
} else {
hash = Sighash.sighash(transaction, sigtype, index, self.redeemScript);
}
return hash;
};
MultiSigScriptHashInput.prototype.getSignatures = function(transaction, privateKey, index, sigtype) {
$.checkState(this.output instanceof Output);
sigtype = sigtype || Signature.SIGHASH_ALL;
@ -70,12 +110,20 @@ MultiSigScriptHashInput.prototype.getSignatures = function(transaction, privateK
var results = [];
_.each(this.publicKeys, function(publicKey) {
if (publicKey.toString() === privateKey.publicKey.toString()) {
var signature;
if (self.nestedWitness) {
var scriptCode = self.getScriptCode();
var satoshisBuffer = self.getSatoshisBuffer();
signature = SighashWitness.sign(transaction, privateKey, sigtype, index, scriptCode, satoshisBuffer);
} else {
signature = Sighash.sign(transaction, privateKey, sigtype, index, self.redeemScript);
}
results.push(new TransactionSignature({
publicKey: privateKey.publicKey,
prevTxId: self.prevTxId,
outputIndex: self.outputIndex,
inputIndex: index,
signature: Sighash.sign(transaction, privateKey, sigtype, index, self.redeemScript),
signature: signature,
sigtype: sigtype
}));
}
@ -94,12 +142,25 @@ MultiSigScriptHashInput.prototype.addSignature = function(transaction, signature
};
MultiSigScriptHashInput.prototype._updateScript = function() {
this.setScript(Script.buildP2SHMultisigIn(
this.publicKeys,
this.threshold,
this._createSignatures(),
{ cachedMultisig: this.redeemScript }
));
if (this.nestedWitness) {
var stack = [
new Buffer(0),
];
var signatures = this._createSignatures();
for (var i = 0; i < signatures.length; i++) {
stack.push(signatures[i]);
}
stack.push(this.redeemScript.toBuffer());
this.setWitnesses(stack);
} else {
var scriptSig = Script.buildP2SHMultisigIn(
this.publicKeys,
this.threshold,
this._createSignatures(),
{ cachedMultisig: this.redeemScript }
);
this.setScript(scriptSig);
}
return this;
};
@ -142,15 +203,29 @@ MultiSigScriptHashInput.prototype.publicKeysWithoutSignature = function() {
};
MultiSigScriptHashInput.prototype.isValidSignature = function(transaction, signature) {
// FIXME: Refactor signature so this is not necessary
signature.signature.nhashtype = signature.sigtype;
return Sighash.verify(
if (this.nestedWitness) {
signature.signature.nhashtype = signature.sigtype;
var scriptCode = this.getScriptCode();
var satoshisBuffer = this.getSatoshisBuffer();
return SighashWitness.verify(
transaction,
signature.signature,
signature.publicKey,
signature.inputIndex,
scriptCode,
satoshisBuffer
);
} else {
// FIXME: Refactor signature so this is not necessary
signature.signature.nhashtype = signature.sigtype;
return Sighash.verify(
transaction,
signature.signature,
signature.publicKey,
signature.inputIndex,
this.redeemScript
);
);
}
};
MultiSigScriptHashInput.OPCODES_SIZE = 7; // serialized size (<=3) + 0 .. N .. M OP_CHECKMULTISIG

149
lib/transaction/sighashwitness.js

@ -0,0 +1,149 @@
'use strict';
/* jshint maxparams:5 */
var Signature = require('../crypto/signature');
var Script = require('../script');
var Output = require('./output');
var BufferReader = require('../encoding/bufferreader');
var BufferWriter = require('../encoding/bufferwriter');
var BN = require('../crypto/bn');
var Hash = require('../crypto/hash');
var ECDSA = require('../crypto/ecdsa');
var $ = require('../util/preconditions');
var _ = require('lodash');
/**
* Returns a buffer of length 32 bytes with the hash that needs to be signed
* for witness programs as defined by:
* https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
*
* @name Signing.sighash
* @param {Transaction} transaction the transaction to sign
* @param {number} sighashType the type of the hash
* @param {number} inputNumber the input index for the signature
* @param {Buffer} scriptCode
* @param {Buffer} satoshisBuffer
*/
var sighash = function sighash(transaction, sighashType, inputNumber, scriptCode, satoshisBuffer) {
/* jshint maxstatements: 50 */
var hashPrevouts;
var hashSequence;
var hashOutputs;
if (!(sighashType & Signature.SIGHASH_ANYONECANPAY)) {
var buffers = [];
for (var n = 0; n < transaction.inputs.length; n++) {
var input = transaction.inputs[n];
var prevTxIdBuffer = new BufferReader(input.prevTxId).readReverse();
buffers.push(prevTxIdBuffer);
var outputIndexBuffer = new Buffer(new Array(4));
outputIndexBuffer.writeUInt32LE(input.outputIndex, 0);
buffers.push(outputIndexBuffer);
}
hashPrevouts = Hash.sha256sha256(Buffer.concat(buffers));
}
if (!(sighashType & Signature.SIGHASH_ANYONECANPAY) &&
(sighashType & 0x1f) !== Signature.SIGHASH_SINGLE && (sighashType & 0x1f) !== Signature.SIGHASH_NONE) {
var sequenceBuffers = [];
for (var m = 0; m < transaction.inputs.length; m++) {
var sequenceBuffer = new Buffer(new Array(4));
sequenceBuffer.writeUInt32LE(transaction.inputs[m].sequenceNumber, 0);
sequenceBuffers.push(sequenceBuffer);
}
hashSequence = Hash.sha256sha256(Buffer.concat(sequenceBuffers));
}
var outputWriter = new BufferWriter();
if ((sighashType & 0x1f) !== Signature.SIGHASH_SINGLE && (sighashType & 0x1f) !== Signature.SIGHASH_NONE) {
for (var p = 0; p < transaction.outputs.length; p++) {
transaction.outputs[p].toBufferWriter(outputWriter);
}
hashOutputs = Hash.sha256sha256(outputWriter.toBuffer());
} else if ((sighashType & 0x1f) === Signature.SIGHASH_SINGLE && inputNumber < transaction.outputs.length) {
transaction.outputs[inputNumber].toBufferWriter(outputWriter);
hashOutputs = Hash.sha256sha256(outputWriter.toBuffer());
}
// Version
var writer = new BufferWriter();
writer.writeUInt32LE(transaction.version);
// Input prevouts/nSequence (none/all, depending on flags)
writer.write(hashPrevouts);
writer.write(hashSequence);
// The input being signed (replacing the scriptSig with scriptCode + amount)
// The prevout may already be contained in hashPrevout, and the nSequence
// may already be contain in hashSequence.
var outpointId = new BufferReader(transaction.inputs[inputNumber].prevTxId).readReverse();
writer.write(outpointId);
writer.writeUInt32LE(transaction.inputs[inputNumber].outputIndex);
writer.write(scriptCode);
writer.write(satoshisBuffer);
writer.writeUInt32LE(transaction.inputs[inputNumber].sequenceNumber);
// Outputs (none/one/all, depending on flags)
writer.write(hashOutputs);
// Locktime
writer.writeUInt32LE(transaction.nLockTime);
// Sighash type
writer.writeInt32LE(sighashType);
return Hash.sha256sha256(writer.toBuffer());
};
/**
* Create a signature
*
* @name Signing.sign
* @param {Transaction} transaction
* @param {PrivateKey} privateKey
* @param {number} sighash
* @param {number} inputIndex
* @param {Script} subscript
* @return {Signature}
*/
function sign(transaction, privateKey, sighashType, inputIndex, scriptCode, satoshisBuffer) {
var hashbuf = sighash(transaction, sighashType, inputIndex, scriptCode, satoshisBuffer);
var sig = ECDSA.sign(hashbuf, privateKey).set({
nhashtype: sighashType
});
return sig;
}
/**
* Verify a signature
*
* @name Signing.verify
* @param {Transaction} transaction
* @param {Signature} signature
* @param {PublicKey} publicKey
* @param {number} inputIndex
* @param {Script} subscript
* @return {boolean}
*/
function verify(transaction, signature, publicKey, inputIndex, scriptCode, satoshisBuffer) {
$.checkArgument(!_.isUndefined(transaction));
$.checkArgument(!_.isUndefined(signature) && !_.isUndefined(signature.nhashtype));
var hashbuf = sighash(transaction, signature.nhashtype, inputIndex, scriptCode, satoshisBuffer);
return ECDSA.verify(hashbuf, signature, publicKey);
}
/**
* @namespace Signing
*/
module.exports = {
sighash: sighash,
sign: sign,
verify: verify
};

132
lib/transaction/transaction.js

@ -13,6 +13,7 @@ var BufferWriter = require('../encoding/bufferwriter');
var Hash = require('../crypto/hash');
var Signature = require('../crypto/signature');
var Sighash = require('./sighash');
var SighashWitness = require('./sighashwitness');
var Address = require('../address');
var UnspentOutput = require('./unspentoutput');
@ -57,7 +58,6 @@ function Transaction(serialized) {
this._newTransaction();
}
}
var CURRENT_VERSION = 1;
var DEFAULT_NLOCKTIME = 0;
var MAX_BLOCK_SIZE = 1000000;
@ -105,6 +105,16 @@ var hashProperty = {
return new BufferReader(this._getHash()).readReverse().toString('hex');
}
};
var witnessHashProperty = {
configurable: false,
enumerable: true,
get: function() {
return new BufferReader(this._getWitnessHash()).readReverse().toString('hex');
}
};
Object.defineProperty(Transaction.prototype, 'witnessHash', witnessHashProperty);
Object.defineProperty(Transaction.prototype, 'hash', hashProperty);
Object.defineProperty(Transaction.prototype, 'id', hashProperty);
@ -126,7 +136,15 @@ Object.defineProperty(Transaction.prototype, 'outputAmount', ioProperty);
* @return {Buffer}
*/
Transaction.prototype._getHash = function() {
return Hash.sha256sha256(this.toBuffer());
return Hash.sha256sha256(this.toBuffer(true));
};
/**
* Retrieve the little endian hash of the transaction including witness data
* @return {Buffer}
*/
Transaction.prototype._getWitnessHash = function() {
return Hash.sha256sha256(this.toBuffer(false));
};
/**
@ -273,21 +291,51 @@ Transaction.prototype.inspect = function() {
return '<Transaction: ' + this.uncheckedSerialize() + '>';
};
Transaction.prototype.toBuffer = function() {
Transaction.prototype.toBuffer = function(noWitness) {
var writer = new BufferWriter();
return this.toBufferWriter(writer).toBuffer();
return this.toBufferWriter(writer, noWitness).toBuffer();
};
Transaction.prototype.toBufferWriter = function(writer) {
Transaction.prototype.hasWitnesses = function() {
for (var i = 0; i < this.inputs.length; i++) {
if (this.inputs[i].hasWitnesses()) {
return true;
}
}
return false;
};
Transaction.prototype.toBufferWriter = function(writer, noWitness) {
writer.writeInt32LE(this.version);
var hasWitnesses = this.hasWitnesses();
if (hasWitnesses && !noWitness) {
writer.write(new Buffer('0001', 'hex'));
}
writer.writeVarintNum(this.inputs.length);
_.each(this.inputs, function(input) {
input.toBufferWriter(writer);
});
writer.writeVarintNum(this.outputs.length);
_.each(this.outputs, function(output) {
output.toBufferWriter(writer);
});
if (hasWitnesses && !noWitness) {
_.each(this.inputs, function(input) {
var witnesses = input.getWitnesses();
writer.writeVarintNum(witnesses.length);
for (var j = 0; j < witnesses.length; j++) {
writer.writeVarintNum(witnesses[j].length);
writer.write(witnesses[j]);
}
});
}
writer.writeUInt32LE(this.nLockTime);
return writer;
};
@ -299,22 +347,46 @@ Transaction.prototype.fromBuffer = function(buffer) {
Transaction.prototype.fromBufferReader = function(reader) {
$.checkArgument(!reader.finished(), 'No transaction data received');
var i, sizeTxIns, sizeTxOuts;
this.version = reader.readInt32LE();
sizeTxIns = reader.readVarintNum();
for (i = 0; i < sizeTxIns; i++) {
var sizeTxIns = reader.readVarintNum();
// check for segwit
var hasWitnesses = false;
if (sizeTxIns === 0 && reader.buf[reader.pos] !== 0) {
reader.pos += 1;
hasWitnesses = true;
sizeTxIns = reader.readVarintNum();
}
for (var i = 0; i < sizeTxIns; i++) {
var input = Input.fromBufferReader(reader);
this.inputs.push(input);
}
sizeTxOuts = reader.readVarintNum();
for (i = 0; i < sizeTxOuts; i++) {
var sizeTxOuts = reader.readVarintNum();
for (var j = 0; j < sizeTxOuts; j++) {
this.outputs.push(Output.fromBufferReader(reader));
}
if (hasWitnesses) {
for (var k = 0; k < sizeTxIns; k++) {
var itemCount = reader.readVarintNum();
var witnesses = [];
for (var l = 0; l < itemCount; l++) {
var size = reader.readVarintNum();
var item = reader.read(size);
witnesses.push(item);
}
this.inputs[k].setWitnesses(witnesses);
}
}
this.nLockTime = reader.readUInt32LE();
return this;
};
Transaction.prototype.toObject = Transaction.prototype.toJSON = function toObject() {
var inputs = [];
this.inputs.forEach(function(input) {
@ -532,8 +604,9 @@ Transaction.prototype._newTransaction = function() {
* @param {(Array.<Transaction~fromObject>|Transaction~fromObject)} utxo
* @param {Array=} pubkeys
* @param {number=} threshold
* @param {boolean=} nestedWitness - Indicates that the utxo is nested witness p2sh
*/
Transaction.prototype.from = function(utxo, pubkeys, threshold) {
Transaction.prototype.from = function(utxo, pubkeys, threshold, nestedWitness) {
if (_.isArray(utxo)) {
var self = this;
_.each(utxo, function(utxo) {
@ -549,7 +622,7 @@ Transaction.prototype.from = function(utxo, pubkeys, threshold) {
return this;
}
if (pubkeys && threshold) {
this._fromMultisigUtxo(utxo, pubkeys, threshold);
this._fromMultisigUtxo(utxo, pubkeys, threshold, nestedWitness);
} else {
this._fromNonP2SH(utxo);
}
@ -577,7 +650,7 @@ Transaction.prototype._fromNonP2SH = function(utxo) {
}));
};
Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold) {
Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold, nestedWitness) {
$.checkArgument(threshold <= pubkeys.length,
'Number of required signatures must be greater than the number of public keys');
var clazz;
@ -597,7 +670,7 @@ Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold) {
prevTxId: utxo.txId,
outputIndex: utxo.outputIndex,
script: Script.empty()
}, pubkeys, threshold));
}, pubkeys, threshold, false, nestedWitness));
};
/**
@ -1121,7 +1194,36 @@ Transaction.prototype.isValidSignature = function(signature) {
/**
* @returns {bool} whether the signature is valid for this transaction input
*/
Transaction.prototype.verifySignature = function(sig, pubkey, nin, subscript) {
Transaction.prototype.verifySignature = function(sig, pubkey, nin, subscript, sigversion, satoshis) {
if (_.isUndefined(sigversion)) {
sigversion = 0;
}
if (sigversion === 1) {
var subscriptBuffer = subscript.toBuffer();
var scriptCodeWriter = new BufferWriter();
scriptCodeWriter.writeVarintNum(subscriptBuffer.length);
scriptCodeWriter.write(subscriptBuffer);
var satoshisBuffer;
if (satoshis) {
$.checkState(JSUtil.isNaturalNumber(satoshis));
satoshisBuffer = new BufferWriter().writeUInt64LEBN(new BN(satoshis)).toBuffer();
} else {
satoshisBuffer = this.inputs[nin].getSatoshisBuffer();
}
var verified = SighashWitness.verify(
this,
sig,
pubkey,
nin,
scriptCodeWriter.toBuffer(),
satoshisBuffer
);
return verified;
}
return Sighash.verify(this, sig, pubkey, nin, subscript);
};

5
test/address.js

@ -551,6 +551,11 @@ describe('Address', function() {
address.toString().should.equal('2N7T3TAetJrSCruQ39aNrJvYLhG1LJosujf');
});
it('can create an address from a set of public keys with a nested witness program', function() {
var address = Address.createMultisig(publics, 2, Networks.livenet, true);
address.toString().should.equal('3PpK1bBqUmPK3Q6QPSUK7BQSZ1DMWL6aes');
});
it('can also be created by Address.createMultisig', function() {
var address = Address.createMultisig(publics, 2);
var address2 = Address.createMultisig(publics, 2);

3
test/data/bitcoind/tx_invalid.json

@ -24,9 +24,6 @@
"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"],
["Tests for CheckTransaction()"],
["No inputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
"0100000000010000000000000000015100000000", "P2SH"],
["No outputs"],
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]],

106
test/script/interpreter.js

@ -1,6 +1,7 @@
'use strict';
var should = require('chai').should();
var sinon = require('sinon');
var bitcore = require('../..');
var Interpreter = bitcore.Script.Interpreter;
var Transaction = bitcore.Transaction;
@ -97,6 +98,111 @@ describe('Interpreter', function() {
});
describe('#verifyWitnessProgram', function() {
it('will return true if witness program greater than 0', function() {
var si = Interpreter();
var version = 1;
var program = new Buffer('bcbd1db07ce89d1f4050645c26c90ce78b67eff78460002a4d5c10410958e064', 'hex');
var witness = [new Buffer('bda0eeeb166c8bfeaee88dedc8efa82d3bea35aac5be253902f59d52908bfe25', 'hex')];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(true);
});
it('will return false with error if witness length is 0', function() {
var si = Interpreter();
var version = 0;
var program = new Buffer('bcbd1db07ce89d1f4050645c26c90ce78b67eff78460002a4d5c10410958e064', 'hex');
var witness = [];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY');
});
it('will return false if program hash mismatch (version 0, 32 byte program)', function() {
var si = Interpreter();
var version = 0;
var program = new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex');
var witness = [
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH');
});
it('will return false if witness stack doesn\'t have two items (version 0, 20 byte program)', function() {
var si = Interpreter();
var version = 0;
var program = new Buffer('b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc6', 'hex');
var witness = [
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH');
});
it('will return false if program wrong length for version 0', function() {
var si = Interpreter();
var version = 0;
var program = new Buffer('b8bcb07f6344b42ab04250c86a6e8b75d3', 'hex');
var witness = [
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH');
});
it('will return false with discourage upgradable witness program', function() {
var si = Interpreter();
var version = 1;
var program = new Buffer('b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc6', 'hex');
var witness = [
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
];
var satoshis = 1;
var flags = Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM');
});
it('will return false with error if stack doesn\'t have exactly one item', function() {
var si = Interpreter();
si.evaluate = sinon.stub().returns(true);
var version = 0;
var program = new Buffer('b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc6', 'hex');
var witness = [
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_EVAL_FALSE');
});
it('will return false if last item in stack casts to false', function() {
var si = Interpreter();
si.evaluate = function() {
si.stack = [new Buffer('00', 'hex')];
return true;
};
var version = 0;
var program = new Buffer('b8bcb07f6344b42ab04250c86a6e8b75d3fdbbc6', 'hex');
var witness = [
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex')
];
var satoshis = 1;
var flags = 0;
si.verifyWitnessProgram(version, program, witness, satoshis, flags).should.equal(false);
si.errstr.should.equal('SCRIPT_ERR_EVAL_FALSE_IN_STACK');
});
});
describe('#verify', function() {
it('should verify these trivial scripts', function() {

66
test/script/script.js

@ -442,6 +442,60 @@ describe('Script', function() {
});
describe('#isWitnessScriptHashOut', function() {
it('should recognize this script as p2wsh', function() {
Script('OP_0 32 0xa99d08fbec6958f4d4a3776c3728ec448934d25fe1142054b8b68bac866dfc3a')
.isWitnessScriptHashOut().should.equal(true);
Script('0020a99d08fbec6958f4d4a3776c3728ec448934d25fe1142054b8b68bac866dfc3a')
.isWitnessScriptHashOut().should.equal(true);
});
it('should NOT identify as p2wsh', function() {
Script('OP_0 20 0x799d283e7f92af1dd242bf4eea513c6efd117de2')
.isWitnessScriptHashOut().should.equal(false);
});
});
describe('#isWitnessPublicKeyHashOut', function() {
it('should identify as p2wpkh', function() {
Script('OP_0 20 0x799d283e7f92af1dd242bf4eea513c6efd117de2')
.isWitnessPublicKeyHashOut().should.equal(true);
Script('0014799d283e7f92af1dd242bf4eea513c6efd117de2').isWitnessPublicKeyHashOut().should.equal(true);
});
it('should NOT identify as p2wpkh', function() {
Script('OP_0 32 0xa99d08fbec6958f4d4a3776c3728ec448934d25fe1142054b8b68bac866dfc3a')
.isWitnessPublicKeyHashOut().should.equal(false);
});
});
describe('#isWitnessProgram', function() {
it('will default values to empty object', function() {
Script('OP_0 20 0x799d283e7f92af1dd242bf4eea513c6efd117de2')
.isWitnessProgram().should.equal(true);
});
it('will return false if script is data push longer than 40 bytes', function() {
Script('OP_0 42 0xd06863c385592423903682926825c495b6cf88fd7cd6159ffd72f778ca475d3046e7b87835d3b457cd')
.isWitnessProgram().should.equal(false);
});
it('will return false if first byte op_code is greater than OP_16', function() {
Script('OP_NOP 20 0x799d283e7f92af1dd242bf4eea513c6efd117de2')
.isWitnessProgram().should.equal(false);
});
it('will return true with datapush of 20', function() {
var values = {};
Script('OP_0 20 0x799d283e7f92af1dd242bf4eea513c6efd117de2')
.isWitnessProgram(values).should.equal(true);
values.version.should.equal(0);
values.program.toString('hex').should.equal('799d283e7f92af1dd242bf4eea513c6efd117de2');
});
it('will return true with datapush of 32', function() {
var values = {};
Script('OP_0 32 0xc756f6d660d4aaad55534cac599a0d9bf5c7e8f70363d22926291811a168c620')
.isWitnessProgram(values).should.equal(true);
values.version.should.equal(0);
values.program.toString('hex').should.equal('c756f6d660d4aaad55534cac599a0d9bf5c7e8f70363d22926291811a168c620');
});
});
describe('#isPushOnly', function() {
it('should know these scripts are or aren\'t push only', function() {
Script('OP_NOP 1 0x01').isPushOnly().should.equal(false);
@ -697,6 +751,18 @@ describe('Script', function() {
}
}
});
describe('#buildWitnessMultisigOutFromScript', function() {
it('it will build nested witness scriptSig', function() {
var redeemScript = bitcore.Script();
var redeemHash = bitcore.crypto.Hash.sha256(redeemScript.toBuffer());
var s = Script.buildWitnessMultisigOutFromScript(redeemScript);
var buf = s.toBuffer();
buf[0].should.equal(0);
buf.slice(2, 34).toString('hex').should.equal(redeemHash.toString('hex'));
});
});
describe('#buildPublicKeyHashOut', function() {
it('should create script from livenet address', function() {
var address = Address.fromString('1NaTVwXDDUJaXDQajoa9MqHhz4uTxtgK14');

33
test/transaction/input/multisigscripthash.js

@ -111,4 +111,37 @@ describe('MultiSigScriptHashInput', function() {
var roundtrip = new MultiSigScriptHashInput(input.toObject());
roundtrip.toObject().should.deep.equal(input.toObject());
});
it('will get the scriptCode for nested witness', function() {
var address = Address.createMultisig([public1, public2, public3], 2, 'testnet', true);
var utxo = {
address: address.toString(),
txId: '66e64ef8a3b384164b78453fa8c8194de9a473ba14f89485a0e433699daec140',
outputIndex: 0,
script: new Script(address),
satoshis: 1000000
};
var transaction = new Transaction()
.from(utxo, [public1, public2, public3], 2, true)
.to(address, 1000000);
var input = transaction.inputs[0];
var scriptCode = input.getScriptCode();
scriptCode.toString('hex').should.equal('695221025c95ec627038e85b5688a9b3d84d28c5ebe66e8c8d697d498e20fe96e3b1ab1d2102cdddfc974d41a62f1f80081deee70592feb7d6e6cf6739d6592edbe7946720e72103c95924e02c240b5545089c69c6432447412b58be43fd671918bd184a5009834353ae');
});
it('will get the satoshis buffer for nested witness', function() {
var address = Address.createMultisig([public1, public2, public3], 2, 'testnet', true);
var utxo = {
address: address.toString(),
txId: '66e64ef8a3b384164b78453fa8c8194de9a473ba14f89485a0e433699daec140',
outputIndex: 0,
script: new Script(address),
satoshis: 1000000
};
var transaction = new Transaction()
.from(utxo, [public1, public2, public3], 2, true)
.to(address, 1000000);
var input = transaction.inputs[0];
var satoshisBuffer = input.getSatoshisBuffer();
satoshisBuffer.toString('hex').should.equal('40420f0000000000');
});
});

24
test/transaction/sighashwitness.js

@ -0,0 +1,24 @@
'use strict';
var chai = require('chai');
var should = chai.should();
var bitcore = require('../../');
var Transaction = bitcore.Transaction;
var Signature = bitcore.crypto.Signature;
var SighashWitness = Transaction.SighashWitness;
describe('Sighash Witness Program Version 0', function() {
it('should create hash for sighash all', function() {
// https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki
var unsignedTx = bitcore.Transaction('0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000');
var scriptCode = new Buffer('1976a9141d0f172a0ecb48aee1be1f2687d2963ae33f71a188ac', 'hex');
var satoshisBuffer = new Buffer('0046c32300000000', 'hex');
var hash = SighashWitness.sighash(unsignedTx, Signature.SIGHASH_ALL, 1, scriptCode, satoshisBuffer);
hash.toString('hex').should.equal('c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670');
});
});

425
test/transaction/transaction.js

@ -14,6 +14,7 @@ var Input = bitcore.Transaction.Input;
var Output = bitcore.Transaction.Output;
var PrivateKey = bitcore.PrivateKey;
var Script = bitcore.Script;
var Interpreter = bitcore.Script.Interpreter;
var Address = bitcore.Address;
var Networks = bitcore.Networks;
var Opcode = bitcore.Opcode;
@ -1235,6 +1236,430 @@ describe('Transaction', function() {
});
});
});
describe('Segregated Witness', function() {
it('identify as segwit transaction', function() {
// https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki
var version = new Buffer('01000000', 'hex');
var marker = new Buffer('00', 'hex'); //always zero
var flag = new Buffer('01', 'hex'); //non zero
var inputCount = new Buffer('01', 'hex');
var inputDummy = new Buffer('2052cda8bc0c2cb743f154881fc85cb675527dcf2f7a5938241020c33341b3f70000000000ffffffff', 'hex');
var outputCount = new Buffer('00', 'hex');
var witness = new Buffer('01', 'hex');
var witnessItems = new Buffer('00', 'hex');
var locktime = new Buffer('00000000', 'hex');
var txBuffer = Buffer.concat([version, marker, flag, inputCount, inputDummy, outputCount, witness,
witnessItems, locktime]);
var tx = bitcore.Transaction().fromBuffer(txBuffer);
tx.hasWitnesses().should.equal(true);
});
it('correctly calculate hash for segwit transaction', function() {
var txBuffer = new Buffer('01000000000101b0e5caa7e37d4b8530c3e1071a36dd5e05d1065cf7224ddff42c69e3387689870000000000ffffffff017b911100000000001600144ff831574da8bef07f8bc97244a1666147b071570247304402203fcbcfddbd6ca3a90252610dd63f1be50b2d926b8d87c912da0a3e42bb03fba002202a90c8aad75da22b0549c72618b754114583e934c0b0d2ccd6c13fcd859ba4ed01210363f3f47f4555779de405eab8d0dc8c2a4f3e09f4171a3fa47c7a77715795319800000000', 'hex');
var tx = bitcore.Transaction().fromBuffer(txBuffer);
tx.hash.should.equal('7f1a2d46746f1bfbb22ab797d5aad1fd9723477b417fa34dff73d8a7dbb14570');
tx.witnessHash.should.equal('3c26fc8b5cfe65f96d955cecfe4d11db2659d052171f9f31af043e9f5073e46b');
});
it('round trip nested witness p2sh', function() {
var txBuffer = new Buffer('010000000001010894bb2bbfd5249b1c55f7bc64352bb64894938bc6439f43f28a58bfa7c73205000000002322002077b16b966ee6a4b8a0901351221d279afd31d3f90df52a3fc53436ea9abde5b0ffffffff01010000000000000000030047304402200fa23efa9a8d6ae285cfc82f81e6c2196d14167553b10da1845abd2c9fe38dc502207a40a58ee5b739e902b275018dfa1bee0d608736ff4317b028fbc29391f4554f01475221037b8dc5861a0ef7b0a97b41d2d1e27186f019d4834dbc99f24952b6f5080f5cce21027152378182102b68b5fce42f9f365ec272c48afda6b0816e735c1dc4b96dd45a52ae00000000', 'hex');
var tx = bitcore.Transaction().fromBuffer(txBuffer);
tx.toBuffer().toString('hex').should.equal(txBuffer.toString('hex'));
});
describe('verifying', function() {
it('will verify these signatures', function() {
var signedTxBuffer = new Buffer('0100000000010103752b9d2baadb95480e2571a4854a68ffd8264462168346461b7cdda76beac20000000023220020fde78ea47ae10cc93c6a850d8a86d8575ddacff38ee9b0bc6535dc016a197068ffffffff010100000000000000000400483045022100ea1508225a6d37c0545d22acaee88d29d1675696953f93d657a419613bcee9b802207b8d80ca8176586878f51e001cb9e92f7640b8c9dc530fabf9087142c752de89014830450221008c6f4a9ebdee89968ec00ecc12fda67442b589296e86bf3e9bde19f4ba923406022048c3409831a55bf61f2d5defffd3b91767643b6c5981cb32338dd7e9f02821b1014752210236c8204d62fd70e7ca206a36d39f9674fa832964d787c60d44250624242bada4210266cd5a3507d6df5346aa42bd23d4c44c079aef0d7a59534758a0dabb82345c2052ae00000000', 'hex');
var unsignedBuffer = new Buffer('0100000000010103752b9d2baadb95480e2571a4854a68ffd8264462168346461b7cdda76beac20000000023220020fde78ea47ae10cc93c6a850d8a86d8575ddacff38ee9b0bc6535dc016a197068ffffffff010100000000000000000300483045022100ea1508225a6d37c0545d22acaee88d29d1675696953f93d657a419613bcee9b802207b8d80ca8176586878f51e001cb9e92f7640b8c9dc530fabf9087142c752de89014752210236c8204d62fd70e7ca206a36d39f9674fa832964d787c60d44250624242bada4210266cd5a3507d6df5346aa42bd23d4c44c079aef0d7a59534758a0dabb82345c2052ae00000000', 'hex');
var signedTx = bitcore.Transaction().fromBuffer(signedTxBuffer);
var signatures = [
{
publicKey: '0236c8204d62fd70e7ca206a36d39f9674fa832964d787c60d44250624242bada4',
prevTxId: 'c2ea6ba7dd7c1b46468316624426d8ff684a85a471250e4895dbaa2b9d2b7503',
outputIndex: 0,
inputIndex: 0,
signature: '3045022100ea1508225a6d37c0545d22acaee88d29d1675696953f93d657a419613bcee9b802207b8d80ca8176586878f51e001cb9e92f7640b8c9dc530fabf9087142c752de89',
sigtype: bitcore.crypto.Signature.SIGHASH_ALL
},
{
publicKey: '0266cd5a3507d6df5346aa42bd23d4c44c079aef0d7a59534758a0dabb82345c20',
prevTxId: 'c2ea6ba7dd7c1b46468316624426d8ff684a85a471250e4895dbaa2b9d2b7503',
outputIndex: 0,
inputIndex: 0,
signature: '30450221008c6f4a9ebdee89968ec00ecc12fda67442b589296e86bf3e9bde19f4ba923406022048c3409831a55bf61f2d5defffd3b91767643b6c5981cb32338dd7e9f02821b1',
sigtype: bitcore.crypto.Signature.SIGHASH_ALL
}
];
var pubkey1 = bitcore.PublicKey('0236c8204d62fd70e7ca206a36d39f9674fa832964d787c60d44250624242bada4');
var pubkey3 = bitcore.PublicKey('0266cd5a3507d6df5346aa42bd23d4c44c079aef0d7a59534758a0dabb82345c20');
var expectedDestScript = bitcore.Script('a914382ead50307554bcdda12e1238368e9f0e10b11787');
var expectedMultiSigString = '52210236c8204d62fd70e7ca206a36d39f9674fa832964d787c60d44250624242bada4210266cd5a3507d6df5346aa42bd23d4c44c079aef0d7a59534758a0dabb82345c2052ae';
var expectedMultiSig = bitcore.Script(expectedMultiSigString);
var multiSig = bitcore.Script.buildMultisigOut([pubkey1, pubkey3], 2, {
noSorting: true
});
multiSig.toBuffer().toString('hex').should.equal(expectedMultiSigString);
var wits = bitcore.Script.buildWitnessMultisigOutFromScript(multiSig);
var expectedWits = bitcore.Script('0020fde78ea47ae10cc93c6a850d8a86d8575ddacff38ee9b0bc6535dc016a197068');
wits.toBuffer().toString('hex').should.equal('0020fde78ea47ae10cc93c6a850d8a86d8575ddacff38ee9b0bc6535dc016a197068');
var address = Address.payingTo(wits);
address.hashBuffer.toString('hex').should.equal('382ead50307554bcdda12e1238368e9f0e10b117');
var destScript = Script.buildScriptHashOut(wits);
destScript.toBuffer().toString('hex').should.equal('a914382ead50307554bcdda12e1238368e9f0e10b11787');
var signedamount = 1;
var input = new Transaction.Input.MultiSigScriptHash({
output: new Output({
script: destScript,
satoshis: signedamount
}),
prevTxId: 'c2ea6ba7dd7c1b46468316624426d8ff684a85a471250e4895dbaa2b9d2b7503',
outputIndex: 0,
script: Script('220020fde78ea47ae10cc93c6a850d8a86d8575ddacff38ee9b0bc6535dc016a197068')
}, [pubkey1, pubkey3], 2, signatures, true);
signedTx.inputs[0] = input;
signedTx.inputs[0]._updateScript();
signedTx.toBuffer().toString('hex').should.equal(signedTxBuffer.toString('hex'));
var interpreter = new Interpreter();
var flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
var check = interpreter.verify(signedTx.inputs[0].script, destScript, signedTx, 0, flags, input.getWitnesses(), signedamount);
check.should.equal(true);
check = interpreter.verify(signedTx.inputs[0].script, destScript, signedTx, 0, flags, input.getWitnesses(), 1999199);
check.should.equal(false);
var valid1 = signedTx.inputs[0].isValidSignature(signedTx, signedTx.inputs[0].signatures[1]);
valid1.should.equal(true);
var valid = signedTx.inputs[0].isValidSignature(signedTx, signedTx.inputs[0].signatures[0]);
valid.should.equal(true);
});
describe('Bitcoin Core tests', function() {
// from bitcoin core tests at src/test/transaction_tests.cpp
it('will verify pay-to-compressed publickey (v0) part 1', function() {
var check;
var flags;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000016001457d5e8f4701ae218576e4fdbcf702e4716808f5f00000000');
var input1 = bitcore.Transaction('01000000000101da3ca8fe74ee2f6cc6ed02927a5fc8e9832f4ff6ad10521598f7985dcd5d17740000000000ffffffff010100000000000000000247304402202eee148a880846e3ebf9b61b5875a0c5121428d272a8336d10bae745ec401042022063b65baea1adc0e7a15801922242ab89d103143071680cfd4ba6072f8685a76c0121031fa0febd51842888a36c43873d1520c5b186894c5ac04520b096f8a3b49f8a5b00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
});
it('will verify pay-to-compressed publickey (v0) part 2', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000016001457d5e8f4701ae218576e4fdbcf702e4716808f5f00000000');
var input2 = bitcore.Transaction('01000000000101cdc27b7132dc20e463d20458aa9d5c38e664ff114ddab8277af4ed859f2b90e20000000000ffffffff0101000000000000000002483045022100db56d1a70244f478a345478be51891b38b9a46140402cddf85b3024ca1652b4b02202c00aaa41ac941ce426ae358aa8372b63aeba945372002c47dc3725d9dca8343012103585c9f7105e09a0abbc60dc72d9d0a456030d0f10f7c47c0616e71c325085cbd00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input2.inputs[0].script;
var witnesses = input2.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify p2sh witness pay-to-compressed pubkey (v0) part 1', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a914ca8abcc57aff5ba3fb36f76fe8e260ce6a08e0bf8700000000');
var input1 = bitcore.Transaction('01000000000101b85d4c861b00d31ac95ae0b2cad8635d8310fb7ca86b44fefcbe2b98c4e905bd000000001716001469f84dbc7f9ae8626aa2d4aee6c73ef726b53ac2ffffffff0101000000000000000002483045022100c0237a5743c684642b26347cf82df0f3b3e91c76aff171f7d065cea305f059a502205c168682630ea4e6bd42627c237207be3d43aeba5c1b8078f3043455bdb6a2270121036240793eedd7e6e53a7c236d069e4d8558f4c6e5950114d7e3d5e1579c93fdf100000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
});
it('will verify p2sh witness pay-to-compressed pubkey (v0) part 2', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a9145675f64cbe03b43fb6d9d42debd207e4be3337db8700000000');
var input2 = bitcore.Transaction('0100000000010104410fc0d228780b20ff790212aef558df008421a110d56d9c9a9b6e5eeb1a680000000017160014b9c556bc9c34cf70d4c253ff86a9eac64e355a25ffffffff0101000000000000000002483045022100dd41426f5eb82ef2b72a0b4e5112022c80045ae4919b2fdef7f438f7ed3c59ee022043494b6f9a9f28d7e5a5c221f92d5325d941722c0ffd00f8be335592015a44d2012103587155d2618b140244799f7a408a85836403f447d51778bdb832088c4a9dd1e300000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input2.inputs[0].script;
var witnesses = input2.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify witness 2-of-2 multisig (part 1)', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff0101000000000000002200204cd0c4dc1a95d8909396d0c1648793fa673518849e1b25259c581ede30e61b7900000000');
var input1 = bitcore.Transaction('010000000001010d81757bb9f141a2d002138e86e54e8cb92b72201b38480a50377913e918612f0000000000ffffffff010100000000000000000300483045022100aa92d26d830b7529d906f7e72c1015b96b067664b68abae2d960a501e76f07780220694f4850e0003cb7e0d08bd4c67ee5fcb604c42684eb805540db5723c4383f780147522102f30bb0258f12a3bbf4fe0b5ada99974d6dbdd06876cb2687a59fa2ea7c7268aa2103d74fd4c6f08e3a4d32dde8e1404d00b2a3d323f94f5c43b4edda962b1f4cb55852ae00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = 0;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify witness 2-of-2 multisig (part 2)', function() {
var flags;
var check;
var interpreter;
var output2 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000022002067b786a598572a1a0fad2f8f48e90c3f2cc89ef110f029f35323b15ba6e9b2f900000000');
var input2 = bitcore.Transaction('01000000000101812d39aa60f01c994c43bc160c87420b6b93bf8db2fe658df45f152250fae9100000000000ffffffff010100000000000000000300483045022100ae56c6d646656366601835e6bc2d151a9974cb1b7cbdeba27cc51ef8c59d2e3f022041e95e80d3e068eb278e31b07f984800869115111c647e2ca32718d26d8e8cd401475221032ac79a7160a0af81d59ffeb914537b1d126a3629271ac1393090c6c9a94bc81e2103eb8129ad88864e7702604ae5b36bad74dbb0f5abfd8ee9ee5def3869756b6c4152ae00000000');
var scriptPubkey = output2.outputs[0].script;
var scriptSig = input2.inputs[0].script;
var witnesses = input2.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = 0;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify witness 2-of-2 multisig (part 3)', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff0101000000000000002200207780f1145ef7ba4e703388c155d94bc399e24345e11c4559e683d5070feeb27400000000');
var input1 = bitcore.Transaction('01000000000101791890e3effa9d4061a984812a90675418d0eb141655c106cce9b4bbbf9a3be00000000000ffffffff010100000000000000000400483045022100db977a31834033466eb103131b1ef9c57d6cea17f9a7eb3f3bafde1d7c1ddff502205ad84c9ca9c4139dce6e8e7850cc09a49ad57197b266814e79a78527ab4a9f950147304402205bd26da7dab9e379019ffd5e76fa77e161090bf577ed875e8e969f06cd66ba0a0220082cf7315ff7dc7aa8f6cebf7e70af1ffa45e63581c08e6fbc4e964035e6326b0147522102f86e3dc39cf9cd6c0eeb5fe25e3abe34273b8e79cc888dd5512001c7dac31b9921032e16a3c764fb6485345d91b39fb6da52c7026b8819e1e7d2f838a0df1445851a52ae00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
});
it('will verify p2sh witness 2-of-2 multisig (part 1)', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a914d0e24dc9fac5cfc616b364797de40f100086e9d58700000000');
var input1 = bitcore.Transaction('010000000001015865ee582f91c2ac646114493c3c39a3b2b08607cd96ba573f4525a01d1f85da000000002322002055423059d7eb9252d1abd6e85a4710c0bb8fabcd48cf9ddd811377557a77fc0dffffffff010100000000000000000300473044022031f9630a8ed776d6cef9ecab58cc9ee384338f4304152d93ac19482ac1ccbc030220616f194c7228484af208433b734b59ec82e21530408ed7a61e896cfefb5c4d6b014752210361424173f5b273fc134ce02a5009b07422b3f4ee63edc82cfd5bba7f72e530732102014ba09ca8cc68720bdf565f55a28b7b845be8ef6a17188b0fddcd55c16d450652ae00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = 0;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify p2sh witness 2-of-2 multisig (part 2)', function() {
var flags;
var check;
var interpreter;
var output2 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a914294b319a1c23951902e25e0147527c8eac3009c68700000000');
var input2 = bitcore.Transaction('01000000000101d93fa44db148929eada630dd419142935c75a72d3678291327ab35d0983b37500000000023220020786e2abd1a684f8337c637f54f6ba3da75b5d75ef96cc7e7369cc69d8ca80417ffffffff010100000000000000000300483045022100b36be4297f2e1d115aba5a5fbb19f6882c61016ba9d6fa01ebb517d14109ec6602207de237433c7534d766ec36d9bddf839b961805e336e42fae574e209b1dc8e30701475221029569b67a4c695502aa31c8a7992b975aa591f2d7de61a4def63771213792288c2103ad3b7eeedf4cba17836ff9a29044a782889cd74ca8f426e83112fa199611676652ae00000000');
var scriptPubkey = output2.outputs[0].script;
var scriptSig = input2.inputs[0].script;
var witnesses = input2.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = 0;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify p2sh witness 2-of-2 multisig (part 3)', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a9143f588990832299c654d8032bc6c5d181427a321e8700000000');
var input1 = bitcore.Transaction('01000000000101ef6f782539d100d563d736339c4a57485b562f9705b28680b08b3efe9dd815870000000023220020a51db581b721c64132415f985ac3086bcf7817f1bbf45be984718b41f4189b39ffffffff01010000000000000000040047304402203202c4c3b40c091a051707421def9adb0d101076672ab220db36a3f87bbecad402205f976ff87af9149e83c87c94ec3b308c1abe4b8c5b3f43c842ebffc22885fc530147304402203c0a50f199774f6393e42ee29d3540cf868441b47efccb11139a357ecd45c5b702205e8442ff34f6f836cd9ad96c158504469db178d63a309d813ba68b86c7293f66014752210334f22ecf25636ba18f8c89e90d38f05036094fe0be48187fb9842374a237b1062102993d85ece51cec8c4d841fce02faa6130f57c811078c5f2a48c204caf12853b552ae00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
});
it('will verify witness pay-to-uncompressed-pubkey (v1) part 1', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000016001449ca7f5980799857e4cc236a288b95dc7e647de200000000');
var input1 = bitcore.Transaction('010000000001014cc98b43a012d8cb56cee7e2011e041c23a622a69a8b97d6f53144e5eb319d1c0000000000ffffffff010100000000000000000248304502210085fb71eecc4b65fd31102bc93f46ec564fce6d22f749ad2d9b4adf4d9477c52602204c4fb00a48bafb4f1c0d7a397d3e0ae12bb8ae394d8b5632e894eafccabf4b160141047dc77183e8fef00c7839a272c4dc2c9b25fb109c0eebe74b27fa98cfd6fa83c76c44a145827bf880162ff7ae48574b5d42595601eee5b8733f1507f028ba401000000000');
var input2 = bitcore.Transaction('0100000000010170ccaf8888099cee3cb869e768f6f24a85838a936cfda787186b179392144cbc0000000000ffffffff010100000000000000000247304402206667f8681ecdc66ad160ff4916c6f3e2946a1eda9e031535475f834c11d5e07c022064360fce49477fa0898b3928eb4503ca71043c67df9229266316961a6bbcc2ef014104a8288183cc741b814a286414ee5fe81ab189ecae5bb1c42794b270c33ac9702ab279fd97a5ed87437659b45197bbd3a87a449fa5b244a6941303683aa68bd11e00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
});
it('will verify witness pay-to-uncompressed-pubkey (v1) part 2', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000016001449ca7f5980799857e4cc236a288b95dc7e647de200000000');
var input2 = bitcore.Transaction('0100000000010170ccaf8888099cee3cb869e768f6f24a85838a936cfda787186b179392144cbc0000000000ffffffff010100000000000000000247304402206667f8681ecdc66ad160ff4916c6f3e2946a1eda9e031535475f834c11d5e07c022064360fce49477fa0898b3928eb4503ca71043c67df9229266316961a6bbcc2ef014104a8288183cc741b814a286414ee5fe81ab189ecae5bb1c42794b270c33ac9702ab279fd97a5ed87437659b45197bbd3a87a449fa5b244a6941303683aa68bd11e00000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input2.inputs[0].script;
var witnesses = input2.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
it('will verify p2sh witness pay-to-uncompressed-pubkey (v1) part 1', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a9147b615f35c476c8f3c555b4d52e54760b2873742f8700000000');
var input1 = bitcore.Transaction('01000000000101160aa337bd325875674904f80d706b4d02cec9888eb2dbae788e18ed01f7712d0000000017160014eff6eebd0dcd3923ca3ab3ea57071fa82ea1faa5ffffffff010100000000000000000247304402205c87348896d3a9de62b1a646c29c4728bec62e384fa16167e302357883c04134022024a98e0fbfde9c24528fbe8f36e05a19a6f37dea16822b80259fcfc8ab2358fb0141048b4e234c057e32d2304697b4d2273679417355bb6bf2d946add731de9719d6801892b6154291ce2cf45c106a6d754c76f81e4316187aa54938af224d9eddb36400000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input1.inputs[0].script;
var witnesses = input1.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;;
check = interpreter.verify(scriptSig, scriptPubkey, input1, 0, flags, witnesses, satoshis);
check.should.equal(true);
});
it('will verify p2sh witness pay-to-uncompressed-pubkey (v1) part 2', function() {
var flags;
var check;
var interpreter;
var output1 = bitcore.Transaction('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff01010000000000000017a9147b615f35c476c8f3c555b4d52e54760b2873742f8700000000');
var input2 = bitcore.Transaction('01000000000101eefb67109c118e958d81f3f98638d48bc6c14eae97cedfce7c397eabb92b4e320000000017160014eff6eebd0dcd3923ca3ab3ea57071fa82ea1faa5ffffffff010100000000000000000247304402200ed4fa4bc8fbae2d1e88bbe8691b21233c23770e5eebf9767853de8579f5790a022015cb3f3dc88720199ee1ed5a9f4cf3186a29a0c361512f03b648c9998b3da7b4014104dfaee8168fe5d1ead2e0c8bb12e2d3ba500ade4f6c4983f3dbe5b70ffeaca1551d43c6c962b69fb8d2f4c02faaf1d4571aae7bbd209df5f3b8cd153e60e1627300000000');
var scriptPubkey = output1.outputs[0].script;
var scriptSig = input2.inputs[0].script;
var witnesses = input2.inputs[0].getWitnesses();
var satoshis = 1;
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(true);
interpreter = new Interpreter();
flags = Interpreter.SCRIPT_VERIFY_P2SH | Interpreter.SCRIPT_VERIFY_WITNESS;;
check = interpreter.verify(scriptSig, scriptPubkey, input2, 0, flags, witnesses, satoshis);
check.should.equal(false);
});
});
});
describe('signing', function() {
var privateKey1 = PrivateKey.fromWIF('cNuW8LX2oeQXfKKCGxajGvqwhCgBtacwTQqiCGHzzKfmpHGY4TE9');
var publicKey1 = p2shPrivateKey1.toPublicKey();
var privateKey2 = PrivateKey.fromWIF('cTtLHt4mv6zuJytSnM7Vd6NLxyNauYLMxD818sBC8PJ1UPiVTRSs');
var publicKey2 = p2shPrivateKey2.toPublicKey();
var privateKey3 = PrivateKey.fromWIF('cQFMZ5gP9CJtUZPc9X3yFae89qaiQLspnftyxxLGvVNvM6tS6mYY');
var publicKey3 = p2shPrivateKey3.toPublicKey();
var address = Address.createMultisig([
publicKey1
], 1, 'testnet', true);
var utxo = {
address: address.toString(),
txId: '1d732950d99f821b8a8d11972ea56000b0666e4d31fa71861ffd80a83797dc61',
outputIndex: 1,
script: Script.buildScriptHashOut(address).toHex(),
satoshis: 1e8
};
it('will sign with nested p2sh witness program', function() {
var tx = new Transaction()
.from(utxo, [publicKey1], 1, true)
.to([{address: 'n3LsXgyStG2CkS2CnWZtDqxTfCnXB8PvD9', satoshis: 50000}])
.fee(150000)
.change('mqWDcnW3jMzthB8qdB9SnFam6N96GDqM4W')
.sign(privateKey1);
var sighash = tx.inputs[0].getSighash(tx, privateKey1, 0, bitcore.crypto.Signature.SIGHASH_ALL);
sighash.toString('hex').should.equal('51b7c5271ae04071a6d3d4c4cde28003d8e9a09e51931ebae4003539767a4955');
tx.toBuffer().toString('hex').should.equal('0100000000010161dc9737a880fd1f8671fa314d6e66b00060a52e97118d8a1b829fd95029731d010000002322002028ba8620c84df12e3283de37d02cfa7bcae3894e118388d6b3ae50f9aeb38798ffffffff0250c30000000000001976a914ef6aa14d8f5ba65a12c327a9659681c44cd821b088acc0d3f205000000001976a9146d8da2015c6d2890896485edd5897b3b2ec9ebb188ac030047304402203fdbd6604939ed9b46bd07bea993b102336a6fbc0a0c987f05b8522a2079037f022064466db4b0c6cc6697a28e0ba9b28c9738ecba56033a60aab7f04d5da2a8241e0125512102feab7deafbdb39885ef92a285dfa0f4ada0feefce43685e6551c95e71496d98051ae00000000');
});
});
});
});

Loading…
Cancel
Save