move bitcoin_utils changes from feature/SON-134
This commit is contained in:
parent
d2c48f97d4
commit
734e34b667
3 changed files with 148 additions and 15 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/crypto/ripemd160.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/io/raw.hpp>
|
||||
|
|
@ -351,7 +352,7 @@ void add_number_to_script(bytes &script, unsigned char data) {
|
|||
add_data_to_script(script, {data});
|
||||
}
|
||||
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data) {
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, uint64_t>> key_data) {
|
||||
int total_weight = 0;
|
||||
bytes result;
|
||||
add_number_to_script(result, 0);
|
||||
|
|
@ -366,7 +367,7 @@ bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> ke
|
|||
result.push_back(OP_ADD);
|
||||
result.push_back(OP_ENDIF);
|
||||
}
|
||||
int threshold_weight = 2 * total_weight / 3;
|
||||
uint64_t threshold_weight = 2 * total_weight / 3;
|
||||
add_number_to_script(result, static_cast<unsigned char>(threshold_weight));
|
||||
result.push_back(OP_GREATERTHAN);
|
||||
return result;
|
||||
|
|
@ -375,6 +376,17 @@ bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> ke
|
|||
/** The Bech32 character set for encoding. */
|
||||
const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Bech32 character set for decoding. */
|
||||
const int8_t charset_rev[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1};
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
bytes cat(bytes x, const bytes &y) {
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
|
|
@ -409,6 +421,20 @@ uint32_t polymod(const bytes &values) {
|
|||
return chk;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
bytes bech32_expand_hrp(const std::string &hrp) {
|
||||
bytes ret;
|
||||
ret.reserve(hrp.size() + 90);
|
||||
ret.resize(hrp.size() * 2 + 1);
|
||||
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||
unsigned char c = hrp[i];
|
||||
ret[i] = c >> 5;
|
||||
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||
}
|
||||
ret[hrp.size()] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||
bytes enc = cat(expand_hrp(hrp), values);
|
||||
|
|
@ -422,8 +448,17 @@ bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool bech32_verify_checksum(const std::string &hrp, const bytes &values) {
|
||||
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return polymod(cat(bech32_expand_hrp(hrp), values)) == 1;
|
||||
}
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string bech32(const std::string &hrp, const bytes &values) {
|
||||
std::string bech32_encode(const std::string &hrp, const bytes &values) {
|
||||
bytes checksum = bech32_checksum(hrp, values);
|
||||
bytes combined = cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
|
|
@ -434,6 +469,33 @@ std::string bech32(const std::string &hrp, const bytes &values) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Bech32 string. */
|
||||
bytes bech32_decode(const std::string &str) {
|
||||
if (str.size() > 90)
|
||||
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||
for (unsigned char c : str) {
|
||||
if (c < 33 || c > 126)
|
||||
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||
}
|
||||
size_t pos = str.rfind('1');
|
||||
if (pos == str.npos || pos == 0 || pos + 7 > str.size())
|
||||
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||
std::string hrp = str.substr(0, pos);
|
||||
bytes values(str.size() - 1 - pos);
|
||||
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
|
||||
unsigned char c = str[i + pos + 1];
|
||||
int8_t rev = (c < 33 || c > 126) ? -1 : charset_rev[c];
|
||||
if (rev == -1)
|
||||
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||
values[i] = rev;
|
||||
}
|
||||
if (!bech32_verify_checksum(hrp, values))
|
||||
FC_THROW("Invalid bech32 string ${a}", ("a", str));
|
||||
return bytes(values.begin(), values.end() - 6);
|
||||
}
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template <int frombits, int tobits, bool pad>
|
||||
bool convertbits(bytes &out, const bytes &in) {
|
||||
|
|
@ -464,10 +526,23 @@ std::string segwit_addr_encode(const std::string &hrp, uint8_t witver, const byt
|
|||
bytes enc;
|
||||
enc.push_back(witver);
|
||||
convertbits<8, 5, true>(enc, witprog);
|
||||
std::string ret = bech32(hrp, enc);
|
||||
std::string ret = bech32_encode(hrp, enc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a SegWit address. */
|
||||
bytes segwit_addr_decode(const std::string &addr) {
|
||||
bytes dec = bech32_decode(addr);
|
||||
if (dec.size() < 1)
|
||||
FC_THROW("Invalid bech32 address ${a}", ("a", addr));
|
||||
bytes conv;
|
||||
if (!convertbits<5, 8, false>(conv, bytes(dec.begin() + 1, dec.end())) ||
|
||||
conv.size() < 2 || conv.size() > 40 || dec[0] > 16 || (dec[0] == 0 && conv.size() != 20 && conv.size() != 32)) {
|
||||
FC_THROW("Invalid bech32 address ${a}", ("a", addr));
|
||||
}
|
||||
return conv;
|
||||
}
|
||||
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) {
|
||||
// calc script hash
|
||||
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||
|
|
@ -476,14 +551,23 @@ std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_networ
|
|||
case (mainnet):
|
||||
return segwit_addr_encode("bc", 0, wp);
|
||||
case (testnet):
|
||||
case (regtest):
|
||||
return segwit_addr_encode("tb", 0, wp);
|
||||
case (regtest):
|
||||
return segwit_addr_encode("bcrt", 0, wp);
|
||||
default:
|
||||
FC_THROW("Unknown bitcoin network type");
|
||||
}
|
||||
FC_THROW("Unknown bitcoin network type");
|
||||
}
|
||||
|
||||
bytes lock_script_from_pw_address(const std::string &address) {
|
||||
bytes result;
|
||||
result.push_back(OP_0);
|
||||
bytes script_hash = segwit_addr_decode(address);
|
||||
add_data_to_script(result, script_hash);
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes lock_script_for_redeem_script(const bytes &script) {
|
||||
bytes result;
|
||||
result.push_back(OP_0);
|
||||
|
|
@ -677,4 +761,26 @@ bytes add_signatures_to_unsigned_tx(const bytes &unsigned_tx, const std::vector<
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string get_weighted_multisig_address(const std::vector<std::pair<std::string, uint64_t>> &public_keys, bitcoin_network network) {
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t>> key_data;
|
||||
for (auto p : public_keys) {
|
||||
fc::ecc::public_key_data kd;
|
||||
fc::from_hex(p.first, kd.begin(), kd.size());
|
||||
key_data.push_back(std::make_pair(fc::ecc::public_key(kd), p.second));
|
||||
}
|
||||
bytes redeem_script = generate_redeem_script(key_data);
|
||||
return p2wsh_address_from_redeem_script(redeem_script, network);
|
||||
}
|
||||
|
||||
bytes get_weighted_multisig_redeem_script(std::vector<std::pair<std::string, uint64_t>> public_keys) {
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t>> key_data;
|
||||
for (auto p : public_keys)
|
||||
{
|
||||
fc::ecc::public_key_data kd;
|
||||
fc::from_hex(p.first, kd.begin(), kd.size());
|
||||
key_data.push_back(std::make_pair(fc::ecc::public_key(kd), p.second));
|
||||
}
|
||||
return generate_redeem_script(key_data);
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
|
|||
|
|
@ -10,9 +10,13 @@ enum bitcoin_network {
|
|||
regtest
|
||||
};
|
||||
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> key_data);
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet);
|
||||
bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, uint64_t>> key_data);
|
||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = regtest);
|
||||
bytes lock_script_for_redeem_script(const bytes &script);
|
||||
bytes lock_script_from_pw_address(const std::string &address);
|
||||
|
||||
std::string get_weighted_multisig_address(const std::vector<std::pair<std::string, uint64_t>> &public_keys, bitcoin_network network = regtest);
|
||||
bytes get_weighted_multisig_redeem_script(std::vector<std::pair<std::string, uint64_t>> public_keys);
|
||||
|
||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||
std::vector<uint64_t> in_amounts,
|
||||
|
|
@ -73,6 +77,19 @@ struct btc_outpoint {
|
|||
};
|
||||
|
||||
struct btc_in {
|
||||
btc_in() = default;
|
||||
btc_in(const btc_in &) = default;
|
||||
btc_in(btc_in &&) = default;
|
||||
btc_in &operator=(const btc_in &) = default;
|
||||
|
||||
btc_in(const std::string &txid, uint32_t out, uint32_t sequence = 0xffffffff) {
|
||||
prevout.n = out;
|
||||
prevout.hash = fc::uint256(txid);
|
||||
// reverse hash due to the different from_hex algo in bitcoin
|
||||
std::reverse(prevout.hash.data(), prevout.hash.data() + prevout.hash.data_size());
|
||||
nSequence = sequence;
|
||||
}
|
||||
|
||||
btc_outpoint prevout;
|
||||
bytes scriptSig;
|
||||
uint32_t nSequence;
|
||||
|
|
@ -83,6 +100,16 @@ struct btc_in {
|
|||
};
|
||||
|
||||
struct btc_out {
|
||||
btc_out() = default;
|
||||
btc_out(const btc_out &) = default;
|
||||
btc_out(btc_out &&) = default;
|
||||
btc_out &operator=(const btc_out &) = default;
|
||||
|
||||
btc_out(const std::string &address, uint64_t amount) :
|
||||
nValue(amount),
|
||||
scriptPubKey(lock_script_from_pw_address(address)) {
|
||||
}
|
||||
|
||||
int64_t nValue;
|
||||
bytes scriptPubKey;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
|
|||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
|
|
@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(pw_transfer)
|
|||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
|
|
@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign)
|
|||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
|
|
@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign)
|
|||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
|
|
@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign2)
|
|||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
|
|
@ -268,7 +268,7 @@ BOOST_AUTO_TEST_CASE(pw_separate_sign2)
|
|||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
|
|
@ -345,7 +345,7 @@ BOOST_AUTO_TEST_CASE(pw_partially_sign)
|
|||
for(auto& key: priv_old)
|
||||
pub_old.push_back(key.get_public_key());
|
||||
// old key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_old;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_old;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_old.push_back(std::make_pair(pub_old[i], i + 1));
|
||||
// redeem script for old PW
|
||||
|
|
@ -370,7 +370,7 @@ BOOST_AUTO_TEST_CASE(pw_partially_sign)
|
|||
for(auto& key: priv_new)
|
||||
pub_new.push_back(key.get_public_key());
|
||||
// new key weights
|
||||
std::vector<std::pair<fc::ecc::public_key, int> > weights_new;
|
||||
std::vector<std::pair<fc::ecc::public_key, uint64_t> > weights_new;
|
||||
for(unsigned i = 0; i < 15; ++i)
|
||||
weights_new.push_back(std::make_pair(pub_new[i], 16 - i));
|
||||
// redeem script for new PW
|
||||
|
|
|
|||
Loading…
Reference in a new issue