Skip to content

Commit

Permalink
Merge pull request #98 from tuxcanfly/bcash-op-datasig
Browse files Browse the repository at this point in the history
Implement OP_CHECKDATASIG{VERIFY}
  • Loading branch information
nodech authored Nov 12, 2018
2 parents a553951 + 6e36bb7 commit f9315a4
Show file tree
Hide file tree
Showing 7 changed files with 659 additions and 27 deletions.
2 changes: 1 addition & 1 deletion lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ class Chain extends AsyncEmitter {
// Magnetic anomaly is enabled
if (mtp >= this.network.block.magneticAnomalyActivationTime) {
state.magneticAnomaly = true;
// TODO: check data sig
state.flags |= Script.flags.VERIFY_CHECKDATASIG;
state.flags |= Script.flags.VERIFY_SIGPUSHONLY;
state.flags |= Script.flags.VERIFY_CLEANSTACK;
}
Expand Down
7 changes: 4 additions & 3 deletions lib/primitives/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -1156,10 +1156,11 @@ class TX {
/**
* Calculate accurate sigop count, taking into account redeem scripts.
* @param {CoinView} view
* @param {VerifyFlags} flags
* @returns {Number} sigop count
*/

getScripthashSigops(view) {
getScripthashSigops(view, flags) {
if (this.isCoinbase())
return 0;

Expand All @@ -1174,7 +1175,7 @@ class TX {
if (!coin.script.isScripthash())
continue;

total += coin.script.getScripthashSigops(input.script);
total += coin.script.getScripthashSigops(input.script, flags);
}

return total;
Expand All @@ -1194,7 +1195,7 @@ class TX {
let cost = this.getLegacySigops();

if (flags & Script.flags.VERIFY_P2SH)
cost += this.getScripthashSigops(view);
cost += this.getScripthashSigops(view, flags);

return cost;
}
Expand Down
25 changes: 18 additions & 7 deletions lib/script/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ exports.opcodes = {
OP_NOP9: 0xb8,
OP_NOP10: 0xb9,

// More Crypto
OP_CHECKDATASIG: 0xba,
OP_CHECKDATASIGVERIFY: 0xbb,

// Custom
OP_INVALIDOPCODE: 0xff
};
Expand Down Expand Up @@ -294,6 +298,10 @@ exports.opcodesByVal = {
0xb8: 'OP_NOP9',
0xb9: 'OP_NOP10',

// More Crypto
0xba: 'OP_CHECKDATASIG',
0xbb: 'OP_CHECKDATASIGVERIFY',

// Custom
0xff: 'OP_INVALIDOPCODE'
};
Expand Down Expand Up @@ -346,7 +354,8 @@ exports.flags = {
VERIFY_NULLFAIL: 1 << 14,
VERIFY_COMPRESSED_PUBKEYTYPE: 1 << 15,
VERIFY_SIGHASH_FORKID: 1 << 16,
VERIFY_REPLAY_PROTECTION: 1 << 17
VERIFY_REPLAY_PROTECTION: 1 << 17,
VERIFY_CHECKDATASIG: 1 << 18
};

/**
Expand Down Expand Up @@ -489,6 +498,7 @@ exports.isHashType = function isHashType(sig) {

/**
* Test a signature to see whether it contains a low S value.
* NOTE: signature MUST exclude sighash byte
* @param {Buffer} sig
* @returns {Boolean}
*/
Expand All @@ -497,7 +507,7 @@ exports.isLowDER = function isLowDER(sig) {
if (!exports.isSignatureEncoding(sig))
return false;

return secp256k1.isLowDER(sig.slice(0, -1));
return secp256k1.isLowDER(sig);
};

/**
Expand Down Expand Up @@ -545,6 +555,7 @@ exports.isCompressedEncoding = function isCompressedEncoding(key) {

/**
* Test a signature to see if it abides by BIP66.
* NOTE: signature MUST exclude sighash byte
* @see https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
* @param {Buffer} sig
* @returns {Boolean}
Expand All @@ -568,18 +579,18 @@ exports.isSignatureEncoding = function isSignatureEncoding(sig) {
// signature)

// Minimum and maximum size constraints.
if (sig.length < 9)
if (sig.length < 8)
return false;

if (sig.length > 73)
if (sig.length > 72)
return false;

// A signature is of type 0x30 (compound).
if (sig[0] !== 0x30)
return false;

// Make sure the length covers the entire signature.
if (sig[1] !== sig.length - 3)
if (sig[1] !== sig.length - 2)
return false;

// Extract the length of the R element.
Expand All @@ -594,7 +605,7 @@ exports.isSignatureEncoding = function isSignatureEncoding(sig) {

// Verify that the length of the signature matches the sum of the length
// of the elements.
if (lenR + lenS + 7 !== sig.length)
if (lenR + lenS + 6 !== sig.length)
return false;

// Check whether the R element is an integer.
Expand Down Expand Up @@ -647,7 +658,7 @@ exports.toASM = function toASM(item, decode) {
return num.toString(10);
}

if (decode && exports.isSignatureEncoding(item)) {
if (decode && exports.isSignatureEncoding(item.slice(0, -1))) {
const type = item[item.length - 1];

let symbol = exports.hashTypeByVal[type & 0x1f] || '';
Expand Down
125 changes: 109 additions & 16 deletions lib/script/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,7 @@ class Script {
|| !(sig[sig.length - 1] & Script.hashType.SIGHASH_FORKID))
subscript.findAndDelete(sig);

validateSignature(sig, flags);
validateTXSignature(sig, flags);
validateKey(key, flags);

let res = false;
Expand Down Expand Up @@ -1166,6 +1166,49 @@ class Script {

break;
}
case opcodes.OP_CHECKDATASIG:
case opcodes.OP_CHECKDATASIGVERIFY: {
// Make sure this remains an error before activation.
if (!(flags & Script.flags.VERIFY_CHECKDATASIG))
throw new ScriptError('BAD_OPCODE', op, ip);

// (sig message pubkey -- bool)
if (stack.length < 3)
throw new ScriptError('INVALID_STACK_OPERATION', op, ip);

const sig = stack.get(-3);
const msg = stack.get(-2);
const key = stack.get(-1);

validateDataSignature(sig, flags);
validateKey(key, flags);

let res = false;

if (sig.length > 0) {
const hash = sha256.digest(msg);
res = secp256k1.verifyDER(hash, sig, key);
}

if (!res && (flags & Script.flags.VERIFY_NULLFAIL)) {
if (sig.length !== 0)
throw new ScriptError('NULLFAIL', op, ip);
}

stack.pop();
stack.pop();
stack.pop();

stack.pushBool(res);

if (op.value === opcodes.OP_CHECKDATASIGVERIFY) {
if (!res)
throw new ScriptError('CHECKDATASIGVERIFY', op, ip);
stack.pop();
}

break;
}
case opcodes.OP_CHECKMULTISIG:
case opcodes.OP_CHECKMULTISIGVERIFY: {
if (!tx)
Expand Down Expand Up @@ -1220,7 +1263,7 @@ class Script {
const sig = stack.get(-isig);
const key = stack.get(-ikey);

validateSignature(sig, flags);
validateTXSignature(sig, flags);
validateKey(key, flags);

if (sig.length > 0) {
Expand Down Expand Up @@ -2320,7 +2363,7 @@ class Script {
if (raw.length === 0)
return false;

if (common.isSignatureEncoding(raw))
if (common.isSignatureEncoding(raw.slice(0, -1)))
return false;

if (common.isKeyEncoding(raw))
Expand Down Expand Up @@ -2438,7 +2481,7 @@ class Script {
* @returns {Number} sigop count
*/

getSigops(accurate) {
getSigops(accurate, flags) {
let total = 0;
let lastOp = -1;

Expand All @@ -2451,6 +2494,12 @@ class Script {
case opcodes.OP_CHECKSIGVERIFY:
total += 1;
break;
case opcodes.OP_CHECKSDATAIG:
case opcodes.OP_CHECKDATASIGVERIFY:
if (flags & Script.flags.VERIFY_CHECKDATASIG) {
total += 1;
}
break;
case opcodes.OP_CHECKMULTISIG:
case opcodes.OP_CHECKMULTISIGVERIFY:
if (accurate && lastOp >= opcodes.OP_1 && lastOp <= opcodes.OP_16)
Expand All @@ -2469,19 +2518,20 @@ class Script {
/**
* Count the sigops in the script, taking into account redeem scripts.
* @param {Script} input - Input script, needed for access to redeem script.
* @param {VerifyFlags} flags
* @returns {Number} sigop count
*/

getScripthashSigops(input) {
getScripthashSigops(input, flags) {
if (!this.isScripthash())
return this.getSigops(true);
return this.getSigops(true, flags);

const redeem = input.getRedeem();

if (!redeem)
return 0;

return redeem.getSigops(true);
return redeem.getSigops(true, flags);
}

/*
Expand Down Expand Up @@ -3229,11 +3279,8 @@ function validateKey(key, flags) {
}

/**
* Test whether the data element is a valid signature based
* on the encoding, S value, and sighash type. Requires
* VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, VERIFY_LOW_S
* and VERIFY_STRING_ENC to be enabled respectively. Note that
* this will allow zero-length signatures.
* Test whether the raw element is a valid signature based
* on the encoding, S value, and sighash type.
* @param {Buffer} sig
* @param {VerifyFlags?} flags
* @returns {Boolean}
Expand All @@ -3244,10 +3291,6 @@ function validateSignature(sig, flags) {
assert(Buffer.isBuffer(sig));
assert(typeof flags === 'number');

// Allow empty sigs
if (sig.length === 0)
return true;

if ((flags & Script.flags.VERIFY_DERSIG)
|| (flags & Script.flags.VERIFY_LOW_S)
|| (flags & Script.flags.VERIFY_STRICTENC)) {
Expand All @@ -3260,6 +3303,31 @@ function validateSignature(sig, flags) {
throw new ScriptError('SIG_HIGH_S');
}

return true;
}

/**
* Test whether the tx element is a valid signature based
* on the encoding, S value, and sighash type. Requires
* VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, VERIFY_LOW_S
* and VERIFY_STRICTENC to be enabled respectively. Note that
* this will allow zero-length signatures.
* @param {Buffer} sig
* @param {VerifyFlags?} flags
* @returns {Boolean}
* @throws {ScriptError}
*/

function validateTXSignature(sig, flags) {
assert(Buffer.isBuffer(sig));
assert(typeof flags === 'number');

// Allow empty sigs
if (sig.length === 0)
return true;

validateSignature(sig.slice(0, -1), flags);

if (flags & Script.flags.VERIFY_STRICTENC) {
if (!common.isHashType(sig))
throw new ScriptError('SIG_HASHTYPE');
Expand All @@ -3277,6 +3345,31 @@ function validateSignature(sig, flags) {
return true;
}

/**
* Test whether the data element is a valid signature based
* on the encoding, S value, and sighash type. Requires
* VERIFY_DERSIG|VERIFY_LOW_S|VERIFY_STRICTENC, and VERIFY_LOW_S
* to be enabled respectively. Note that this will allow zero-length
* signatures.
* @param {Buffer} sig
* @param {VerifyFlags?} flags
* @returns {Boolean}
* @throws {ScriptError}
*/

function validateDataSignature(sig, flags) {
assert(Buffer.isBuffer(sig));
assert(typeof flags === 'number');

// Allow empty sigs
if (sig.length === 0)
return true;

validateSignature(sig, flags);

return true;
}

/**
* Verify a signature, taking into account sighash type.
* @param {Buffer} msg - Signature hash.
Expand Down
Loading

0 comments on commit f9315a4

Please sign in to comment.