From 36a5d5d0f800007c6f23c4fcf68ac572003abda0 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Tue, 19 Mar 2019 20:31:58 +0000 Subject: [PATCH] Fix bug in calculate_contract for Liquid --- src/pegins.cpp | 117 ++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/src/pegins.cpp b/src/pegins.cpp index b7a457e33a..133395ac45 100644 --- a/src/pegins.cpp +++ b/src/pegins.cpp @@ -75,67 +75,72 @@ CScript calculate_contract(const CScript& federation_script, const CScript& scri std::vector > solutions; unsigned int required; std::vector> keys; + bool is_liquidv1_watchman = MatchLiquidWatchman(federation_script); // 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); + if (!is_liquidv1_watchman && + federation_script != CScript() << OP_TRUE && + !MatchMultisig(federation_script, required, keys)) { + assert(false); } + CScript::const_iterator sdpc = federation_script.begin(); + std::vector vch; + opcodetype opcodeTmp; + bool liquid_op_else_found = false; + while (federation_script.GetOp(sdpc, opcodeTmp, vch)) { - CScript::const_iterator sdpc = federation_script.begin(); - std::vector vch; - opcodetype opcodeTmp; - while (federation_script.GetOp(sdpc, opcodeTmp, vch)) + // For liquid watchman template, don't tweak emergency keys + if (is_liquidv1_watchman && opcodeTmp == OP_ELSE) { + liquid_op_else_found = true; + } + + size_t pub_len = 33; + if (vch.size() == pub_len && !liquid_op_else_found) { - size_t pub_len = 33; - if (vch.size() == pub_len) - { - unsigned char tweak[32]; - CHMAC_SHA256(vch.data(), pub_len).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(tweak); - int ret; - secp256k1_pubkey watchman; - secp256k1_pubkey tweaked; - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &watchman, vch.data(), pub_len); - assert(ret == 1); - ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &tweaked, vch.data(), pub_len); - assert(ret == 1); - // If someone creates a tweak that makes this fail, they broke SHA256 - ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_validation, &tweaked, tweak); - assert(ret == 1); - unsigned char new_pub[33]; - ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_validation, new_pub, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED); - assert(ret == 1); - assert(pub_len == 33); - - // push tweaked pubkey - std::vector pub_vec(new_pub, new_pub + pub_len); - scriptDestination << pub_vec; - - // Sanity checks to reduce pegin risk. If the tweaked - // value flips a bit, we may lose pegin funds irretrievably. - // We take the tweak, derive its pubkey and check that - // `tweaked - watchman = tweak` to check the computation - // two different ways - secp256k1_pubkey tweaked2; - ret = secp256k1_ec_pubkey_create(secp256k1_ctx_validation, &tweaked2, tweak); - assert(ret); - ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_validation, &watchman); - assert(ret); - secp256k1_pubkey* pubkey_combined[2]; - pubkey_combined[0] = &watchman; - pubkey_combined[1] = &tweaked; - secp256k1_pubkey maybe_tweaked2; - ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_validation, &maybe_tweaked2, pubkey_combined, 2); - assert(ret); - assert(!memcmp(&maybe_tweaked2, &tweaked2, 64)); + unsigned char tweak[32]; + CHMAC_SHA256(vch.data(), pub_len).Write(scriptPubKey.data(), scriptPubKey.size()).Finalize(tweak); + int ret; + secp256k1_pubkey watchman; + secp256k1_pubkey tweaked; + ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &watchman, vch.data(), pub_len); + assert(ret == 1); + ret = secp256k1_ec_pubkey_parse(secp256k1_ctx_validation, &tweaked, vch.data(), pub_len); + assert(ret == 1); + // If someone creates a tweak that makes this fail, they broke SHA256 + ret = secp256k1_ec_pubkey_tweak_add(secp256k1_ctx_validation, &tweaked, tweak); + assert(ret == 1); + unsigned char new_pub[33]; + ret = secp256k1_ec_pubkey_serialize(secp256k1_ctx_validation, new_pub, &pub_len, &tweaked, SECP256K1_EC_COMPRESSED); + assert(ret == 1); + assert(pub_len == 33); + + // push tweaked pubkey + std::vector pub_vec(new_pub, new_pub + pub_len); + scriptDestination << pub_vec; + + // Sanity checks to reduce pegin risk. If the tweaked + // value flips a bit, we may lose pegin funds irretrievably. + // We take the tweak, derive its pubkey and check that + // `tweaked - watchman = tweak` to check the computation + // two different ways + secp256k1_pubkey tweaked2; + ret = secp256k1_ec_pubkey_create(secp256k1_ctx_validation, &tweaked2, tweak); + assert(ret); + ret = secp256k1_ec_pubkey_negate(secp256k1_ctx_validation, &watchman); + assert(ret); + secp256k1_pubkey* pubkey_combined[2]; + pubkey_combined[0] = &watchman; + pubkey_combined[1] = &tweaked; + secp256k1_pubkey maybe_tweaked2; + ret = secp256k1_ec_pubkey_combine(secp256k1_ctx_validation, &maybe_tweaked2, pubkey_combined, 2); + assert(ret); + assert(!memcmp(&maybe_tweaked2, &tweaked2, 64)); + } else { + // add to script untouched + if (vch.size() > 0) { + scriptDestination << vch; } else { - // add to script untouched - if (vch.size() > 0) { - scriptDestination << vch; - } else { - scriptDestination << opcodeTmp; - } + scriptDestination << opcodeTmp; } } } @@ -441,5 +446,5 @@ bool MatchLiquidWatchman(const CScript& script) return false; } // No more pushes - return (it + 1 == script.end()); + return (it == script.end()); }