Fix bug in calculate_contract for Liquid

This commit is contained in:
Steven Roose 2019-03-19 20:31:58 +00:00
parent 77ba5eec41
commit 36a5d5d0f8
No known key found for this signature in database
GPG key ID: 7FC91380BB4CE800

View file

@ -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);
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;
// 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));
// 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());
}