Skip to content

Commit 592a622

Browse files
committed
Add detection of liquidv1 fedpeg script, startup sanity check
1 parent 32aec9a commit 592a622

File tree

6 files changed

+96
-8
lines changed

6 files changed

+96
-8
lines changed

src/pegins.cpp

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,20 +70,23 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, u
7070
}
7171

7272
// Takes federation redeem script and adds HMAC_SHA256(pubkey, scriptPubKey) as a tweak to each pubkey
73-
CScript calculate_contract(const CScript& federationRedeemScript, const CScript& scriptPubKey) {
73+
CScript calculate_contract(const CScript& federation_script, const CScript& scriptPubKey) {
7474
CScript scriptDestination;
75-
txnouttype type;
7675
std::vector<std::vector<unsigned char> > solutions;
77-
// Sanity check fedRedeemScript
78-
if (!Solver(federationRedeemScript, type, solutions) || (type != TX_MULTISIG && type != TX_TRUE)) {
76+
unsigned int required;
77+
std::vector<std::vector<unsigned char>> keys;
78+
// Sanity check federation_script only to match 3 templates
79+
if (federation_script != CScript() << OP_TRUE &&
80+
!MatchMultisig(federation_script, required, keys) &&
81+
!MatchLiquidWatchman(federation_script)) {
7982
assert(false);
8083
}
8184

8285
{
83-
CScript::const_iterator sdpc = federationRedeemScript.begin();
86+
CScript::const_iterator sdpc = federation_script.begin();
8487
std::vector<unsigned char> vch;
8588
opcodetype opcodeTmp;
86-
while (federationRedeemScript.GetOp(sdpc, opcodeTmp, vch))
89+
while (federation_script.GetOp(sdpc, opcodeTmp, vch))
8790
{
8891
size_t pub_len = 33;
8992
if (vch.size() == pub_len)
@@ -352,3 +355,75 @@ CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness) {
352355
//return CTxOut(CAsset(pegin_witness.stack[1]), value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
353356
return CTxOut(value, CScript(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end()));
354357
}
358+
359+
bool MatchLiquidWatchman(const CScript& script)
360+
{
361+
CScript::const_iterator it = script.begin();
362+
std::vector<unsigned char> data;
363+
opcodetype opcode;
364+
365+
// Stack depth check for branch choice
366+
if (!script.GetOp(it, opcode, data) || opcode != OP_DEPTH) {
367+
return false;
368+
}
369+
// Take in value, then check equality
370+
if (!script.GetOp(it, opcode, data) ||
371+
!script.GetOp(it, opcode, data) ||
372+
opcode != OP_EQUAL) {
373+
return false;
374+
}
375+
// IF EQUAL
376+
if (!script.GetOp(it, opcode, data) || opcode != OP_IF) {
377+
return false;
378+
}
379+
// Take in value k, make sure minimally encoded number from 1 to 16
380+
if (!script.GetOp(it, opcode, data) ||
381+
opcode > OP_16 ||
382+
(opcode < OP_1NEGATE && !CheckMinimalPush(data, opcode))) {
383+
return false;
384+
}
385+
opcodetype opcode2 = opcode;
386+
std::vector<unsigned char> num = data;
387+
// Iterate through multisig stuff until ELSE is hit
388+
while (opcode != OP_ELSE) {
389+
if (!script.GetOp(it, opcode, data)) {
390+
return false;
391+
}
392+
}
393+
// Take minimally-encoded CSV push number k'
394+
if (!script.GetOp(it, opcode, data) ||
395+
opcode > OP_16 || (opcode < OP_1NEGATE && !CheckMinimalPush(data, opcode))) {
396+
return false;
397+
}
398+
// CSV
399+
if (!script.GetOp(it, opcode, data) || opcode != OP_CHECKSEQUENCEVERIFY) {
400+
return false;
401+
}
402+
// Drop the CSV number
403+
if (!script.GetOp(it, opcode, data) || opcode != OP_DROP) {
404+
return false;
405+
}
406+
// Take the minimally-encoded n of k-of-n multisig arg
407+
if (!script.GetOp(it, opcode, data) ||
408+
opcode > OP_16 || (opcode < OP_1NEGATE && !CheckMinimalPush(data, opcode)) ) {
409+
return false;
410+
}
411+
412+
// The two multisig k-numbers must not match, otherwise ELSE branch can not be reached
413+
if (opcode == opcode2 && num == data) {
414+
return false;
415+
}
416+
417+
// Find the ENDIF
418+
while (opcode != OP_ENDIF) {
419+
if (!script.GetOp(it, opcode, data)) {
420+
return false;
421+
}
422+
}
423+
// CHECKMULTISIG
424+
if (!script.GetOp(it, opcode, data) || opcode != OP_CHECKMULTISIG) {
425+
return false;
426+
}
427+
// No more pushes
428+
return (it + 1 == script.end());
429+
}

src/pegins.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,13 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const COutPoint& p
2222
// Constructs unblinded output to be used in amount and scriptpubkey checks during pegin
2323
CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness);
2424

25+
/* Belt-and-suspenders-only matching against telescoped multisig used on Liquid v1:
26+
* Pseudo-structure:
27+
* Check number of elements on stack
28+
* If enough for federation multisig, push all multisig args onto stack except OP_CMS
29+
* If not, check CSV timeout, then if successful, push emergency key multisig args on
30+
* stack except OP_CMS. End if, then push OP_CMS.
31+
*/
32+
bool MatchLiquidWatchman(const CScript& script);
33+
2534
#endif // BITCOIN_PEGINS_H

src/script/interpreter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ bool static CheckPubKeyEncoding(const valtype &vchPubKey, unsigned int flags, co
233233
return true;
234234
}
235235

236-
bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
236+
bool CheckMinimalPush(const valtype& data, opcodetype opcode) {
237237
// Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal
238238
assert(0 <= opcode && opcode <= OP_PUSHDATA4);
239239
if (data.size() == 0) {

src/script/interpreter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,6 @@ size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey,
196196

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

199+
bool CheckMinimalPush(const std::vector<unsigned char>& data, opcodetype opcode);
200+
199201
#endif // BITCOIN_SCRIPT_INTERPRETER_H

src/script/standard.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static constexpr bool IsSmallInteger(opcodetype opcode)
7272
return opcode >= OP_1 && opcode <= OP_16;
7373
}
7474

75-
static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
75+
bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
7676
{
7777
opcodetype opcode;
7878
valtype data;

src/script/standard.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,6 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
203203
*/
204204
CScript GetScriptForWitness(const CScript& redeemscript);
205205

206+
bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<std::vector<unsigned char>>& pubkeys);
207+
206208
#endif // BITCOIN_SCRIPT_STANDARD_H

0 commit comments

Comments
 (0)