Skip to content

Commit

Permalink
Fix bug in calculate_contract for Liquid
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenroose committed Mar 20, 2019
1 parent 77ba5ee commit 36a5d5d
Showing 1 changed file with 61 additions and 56 deletions.
117 changes: 61 additions & 56 deletions src/pegins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,67 +75,72 @@ CScript calculate_contract(const CScript& federation_script, const CScript& scri
std::vector<std::vector<unsigned char> > solutions;
unsigned int required;
std::vector<std::vector<unsigned char>> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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;
}
}
}
Expand Down Expand Up @@ -441,5 +446,5 @@ bool MatchLiquidWatchman(const CScript& script)
return false;
}
// No more pushes
return (it + 1 == script.end());
return (it == script.end());
}

0 comments on commit 36a5d5d

Please sign in to comment.