Skip to content

Commit

Permalink
Merge #497: [0.17] Add Liquid v1 based script matching functionality
Browse files Browse the repository at this point in the history
592a622 Add detection of liquidv1 fedpeg script, startup sanity check (Gregory Sanders)

Pull request description:

  And use it to sanity check startup constants.

Tree-SHA512: 1e4c3910c5c269384b5dab7a3f38646356a8de7684515324320d610915466e5015e9993d7a2a619473ea3500f3f9145526414548d6da6ee22b4417b6836042bf
  • Loading branch information
stevenroose committed Jan 24, 2019
2 parents fcec315 + 592a622 commit 4d70732
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 8 deletions.
87 changes: 81 additions & 6 deletions src/pegins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,23 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, u
}

// Takes federation redeem script and adds HMAC_SHA256(pubkey, scriptPubKey) as a tweak to each pubkey
CScript calculate_contract(const CScript& federationRedeemScript, const CScript& scriptPubKey) {
CScript calculate_contract(const CScript& federation_script, const CScript& scriptPubKey) {
CScript scriptDestination;
txnouttype type;
std::vector<std::vector<unsigned char> > solutions;
// Sanity check fedRedeemScript
if (!Solver(federationRedeemScript, type, solutions) || (type != TX_MULTISIG && type != TX_TRUE)) {
unsigned int required;
std::vector<std::vector<unsigned char>> keys;
// Sanity check federation_script only to match 3 templates
if (federation_script != CScript() << OP_TRUE &&
!MatchMultisig(federation_script, required, keys) &&
!MatchLiquidWatchman(federation_script)) {
assert(false);
}

{
CScript::const_iterator sdpc = federationRedeemScript.begin();
CScript::const_iterator sdpc = federation_script.begin();
std::vector<unsigned char> vch;
opcodetype opcodeTmp;
while (federationRedeemScript.GetOp(sdpc, opcodeTmp, vch))
while (federation_script.GetOp(sdpc, opcodeTmp, vch))
{
size_t pub_len = 33;
if (vch.size() == pub_len)
Expand Down Expand Up @@ -352,3 +355,75 @@ CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness) {
//return CTxOut(CAsset(pegin_witness.stack[1]), value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
return CTxOut(value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
}

bool MatchLiquidWatchman(const CScript& script)
{
CScript::const_iterator it = script.begin();
std::vector<unsigned char> data;
opcodetype opcode;

// Stack depth check for branch choice
if (!script.GetOp(it, opcode, data) || opcode != OP_DEPTH) {
return false;
}
// Take in value, then check equality
if (!script.GetOp(it, opcode, data) ||
!script.GetOp(it, opcode, data) ||
opcode != OP_EQUAL) {
return false;
}
// IF EQUAL
if (!script.GetOp(it, opcode, data) || opcode != OP_IF) {
return false;
}
// Take in value k, make sure minimally encoded number from 1 to 16
if (!script.GetOp(it, opcode, data) ||
opcode > OP_16 ||
(opcode < OP_1NEGATE && !CheckMinimalPush(data, opcode))) {
return false;
}
opcodetype opcode2 = opcode;
std::vector<unsigned char> num = data;
// Iterate through multisig stuff until ELSE is hit
while (opcode != OP_ELSE) {
if (!script.GetOp(it, opcode, data)) {
return false;
}
}
// Take minimally-encoded CSV push number k'
if (!script.GetOp(it, opcode, data) ||
opcode > OP_16 || (opcode < OP_1NEGATE && !CheckMinimalPush(data, opcode))) {
return false;
}
// CSV
if (!script.GetOp(it, opcode, data) || opcode != OP_CHECKSEQUENCEVERIFY) {
return false;
}
// Drop the CSV number
if (!script.GetOp(it, opcode, data) || opcode != OP_DROP) {
return false;
}
// Take the minimally-encoded n of k-of-n multisig arg
if (!script.GetOp(it, opcode, data) ||
opcode > OP_16 || (opcode < OP_1NEGATE && !CheckMinimalPush(data, opcode)) ) {
return false;
}

// The two multisig k-numbers must not match, otherwise ELSE branch can not be reached
if (opcode == opcode2 && num == data) {
return false;
}

// Find the ENDIF
while (opcode != OP_ENDIF) {
if (!script.GetOp(it, opcode, data)) {
return false;
}
}
// CHECKMULTISIG
if (!script.GetOp(it, opcode, data) || opcode != OP_CHECKMULTISIG) {
return false;
}
// No more pushes
return (it + 1 == script.end());
}
9 changes: 9 additions & 0 deletions src/pegins.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
// Constructs unblinded output to be used in amount and scriptpubkey checks during pegin
CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness);

/* Belt-and-suspenders-only matching against telescoped multisig used on Liquid v1:
* Pseudo-structure:
* Check number of elements on stack
* If enough for federation multisig, push all multisig args onto stack except OP_CMS
* If not, check CSV timeout, then if successful, push emergency key multisig args on
* stack except OP_CMS. End if, then push OP_CMS.
*/
bool MatchLiquidWatchman(const CScript& script);

#endif // BITCOIN_PEGINS_H
2 changes: 1 addition & 1 deletion src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co
return true;
}

bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
bool CheckMinimalPush(const valtype& data, opcodetype opcode) {
// Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal
assert(0 <= opcode && opcode <= OP_PUSHDATA4);
if (data.size() == 0) {
Expand Down
2 changes: 2 additions & 0 deletions src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,6 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,

int FindAndDelete(CScript& script, const CScript& b);

bool CheckMinimalPush(const std::vector<unsigned char>& data, opcodetype opcode);

#endif // BITCOIN_SCRIPT_INTERPRETER_H
2 changes: 1 addition & 1 deletion src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ static constexpr bool IsSmallInteger(opcodetype opcode)
return opcode >= OP_1 && opcode <= OP_16;
}

static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
{
opcodetype opcode;
valtype data;
Expand Down
2 changes: 2 additions & 0 deletions src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,6 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
*/
CScript GetScriptForWitness(const CScript& redeemscript);

bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<std::vector<unsigned char>>& pubkeys);

#endif // BITCOIN_SCRIPT_STANDARD_H

0 comments on commit 4d70732

Please sign in to comment.