replace createrawtransaction call
This commit is contained in:
parent
0d1adde0a6
commit
6b47ed4a8c
3 changed files with 163 additions and 41 deletions
|
|
@ -375,6 +375,18 @@ bytes generate_redeem_script(std::vector<std::pair<fc::ecc::public_key, int>> ke
|
||||||
/** The Bech32 character set for encoding. */
|
/** The Bech32 character set for encoding. */
|
||||||
const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
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. */
|
/** Concatenate two byte arrays. */
|
||||||
bytes cat(bytes x, const bytes &y) {
|
bytes cat(bytes x, const bytes &y) {
|
||||||
x.insert(x.end(), y.begin(), y.end());
|
x.insert(x.end(), y.begin(), y.end());
|
||||||
|
|
@ -409,6 +421,21 @@ uint32_t polymod(const bytes &values) {
|
||||||
return chk;
|
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. */
|
/** Create a checksum. */
|
||||||
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||||
bytes enc = cat(expand_hrp(hrp), values);
|
bytes enc = cat(expand_hrp(hrp), values);
|
||||||
|
|
@ -422,8 +449,18 @@ bytes bech32_checksum(const std::string &hrp, const bytes &values) {
|
||||||
return ret;
|
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. */
|
/** 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 checksum = bech32_checksum(hrp, values);
|
||||||
bytes combined = cat(values, checksum);
|
bytes combined = cat(values, checksum);
|
||||||
std::string ret = hrp + '1';
|
std::string ret = hrp + '1';
|
||||||
|
|
@ -434,6 +471,33 @@ std::string bech32(const std::string &hrp, const bytes &values) {
|
||||||
return ret;
|
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. */
|
/** Convert from one power-of-2 number base to another. */
|
||||||
template <int frombits, int tobits, bool pad>
|
template <int frombits, int tobits, bool pad>
|
||||||
bool convertbits(bytes &out, const bytes &in) {
|
bool convertbits(bytes &out, const bytes &in) {
|
||||||
|
|
@ -460,15 +524,32 @@ bool convertbits(bytes &out, const bytes &in) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Encode a SegWit address. */
|
/** Encode a SegWit address. */
|
||||||
std::string segwit_addr_encode(const std::string &hrp, uint8_t witver, const bytes &witprog) {
|
std::string segwit_addr_encode(const std::string& hrp, uint8_t witver, const bytes& witprog)
|
||||||
bytes enc;
|
{
|
||||||
enc.push_back(witver);
|
bytes enc;
|
||||||
convertbits<8, 5, true>(enc, witprog);
|
enc.push_back(witver);
|
||||||
std::string ret = bech32(hrp, enc);
|
convertbits<8, 5, true>(enc, witprog);
|
||||||
return ret;
|
std::string ret = bech32_encode(hrp, enc);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network) {
|
/** 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
|
// calc script hash
|
||||||
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
fc::sha256 sh = fc::sha256::hash(reinterpret_cast<const char *>(&script[0]), script.size());
|
||||||
bytes wp(sh.data(), sh.data() + sh.data_size());
|
bytes wp(sh.data(), sh.data() + sh.data_size());
|
||||||
|
|
@ -484,6 +565,14 @@ std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_networ
|
||||||
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 lock_script_for_redeem_script(const bytes &script) {
|
||||||
bytes result;
|
bytes result;
|
||||||
result.push_back(OP_0);
|
result.push_back(OP_0);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ enum bitcoin_network {
|
||||||
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, int>> key_data);
|
||||||
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet);
|
std::string p2wsh_address_from_redeem_script(const bytes &script, bitcoin_network network = mainnet);
|
||||||
bytes lock_script_for_redeem_script(const bytes &script);
|
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);
|
std::string get_weighted_multisig_address(const std::vector<std::pair<std::string, uint64_t>>& public_keys);
|
||||||
|
|
||||||
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
std::vector<bytes> signatures_for_raw_transaction(const bytes &unsigned_tx,
|
||||||
|
|
@ -74,6 +76,20 @@ struct btc_outpoint {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct btc_in {
|
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;
|
btc_outpoint prevout;
|
||||||
bytes scriptSig;
|
bytes scriptSig;
|
||||||
uint32_t nSequence;
|
uint32_t nSequence;
|
||||||
|
|
@ -84,6 +100,17 @@ struct btc_in {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct btc_out {
|
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;
|
int64_t nValue;
|
||||||
bytes scriptPubKey;
|
bytes scriptPubKey;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
|
|
||||||
#include <fc/crypto/base64.hpp>
|
#include <fc/crypto/base64.hpp>
|
||||||
|
#include <fc/crypto/hex.hpp>
|
||||||
#include <fc/log/logger.hpp>
|
#include <fc/log/logger.hpp>
|
||||||
#include <fc/network/ip.hpp>
|
#include <fc/network/ip.hpp>
|
||||||
|
|
||||||
|
|
@ -754,10 +755,20 @@ std::string sidechain_net_handler_bitcoin::transfer_all_btc(const std::string &f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fc::flat_map<std::string, double> outs;
|
btc_tx tx;
|
||||||
outs[to_address] = total_amount - min_amount;
|
tx.hasWitness = true;
|
||||||
|
tx.nVersion = 2;
|
||||||
|
tx.nLockTime = 0;
|
||||||
|
for(const auto& utx: unspent_utxo)
|
||||||
|
{
|
||||||
|
tx.vin.push_back(btc_in(utx.txid_, utx.out_num_));
|
||||||
|
}
|
||||||
|
tx.vout.push_back(btc_out(to_address, uint64_t((total_amount - min_amount) * 100000000.0)));
|
||||||
|
|
||||||
std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs);
|
bytes unsigned_tx;
|
||||||
|
tx.to_bytes(unsigned_tx);
|
||||||
|
|
||||||
|
std::string reply_str = fc::to_hex((char*)&unsigned_tx[0], unsigned_tx.size());
|
||||||
return sign_and_send_transaction_with_wallet(reply_str);
|
return sign_and_send_transaction_with_wallet(reply_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -768,13 +779,7 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(co
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
|
std::string pw_address = obj->addresses.find(sidechain_type::bitcoin)->second;
|
||||||
|
|
||||||
std::stringstream ss(pw_address_json);
|
|
||||||
boost::property_tree::ptree json;
|
|
||||||
boost::property_tree::read_json(ss, json);
|
|
||||||
|
|
||||||
std::string pw_address = json.get<std::string>("address");
|
|
||||||
|
|
||||||
std::string txid = swdo.sidechain_transaction_id;
|
std::string txid = swdo.sidechain_transaction_id;
|
||||||
std::string suid = swdo.sidechain_uid;
|
std::string suid = swdo.sidechain_uid;
|
||||||
|
|
@ -784,20 +789,18 @@ std::string sidechain_net_handler_bitcoin::transfer_deposit_to_primary_wallet(co
|
||||||
uint64_t min_fee_rate = 1000;
|
uint64_t min_fee_rate = 1000;
|
||||||
fee_rate = std::max(fee_rate, min_fee_rate);
|
fee_rate = std::max(fee_rate, min_fee_rate);
|
||||||
deposit_amount -= fee_rate; // Deduct minimum relay fee
|
deposit_amount -= fee_rate; // Deduct minimum relay fee
|
||||||
double transfer_amount = (double)deposit_amount / 100000000.0;
|
|
||||||
|
|
||||||
std::vector<btc_txout> ins;
|
btc_tx tx;
|
||||||
fc::flat_map<std::string, double> outs;
|
tx.nVersion = 2;
|
||||||
|
tx.nLockTime = 0;
|
||||||
|
tx.hasWitness = true;
|
||||||
|
tx.vin.push_back(btc_in(txid, std::stoul(nvout)));
|
||||||
|
tx.vout.push_back(btc_out(pw_address, deposit_amount));
|
||||||
|
|
||||||
btc_txout utxo;
|
bytes unsigned_tx;
|
||||||
utxo.txid_ = txid;
|
tx.to_bytes(unsigned_tx);
|
||||||
utxo.out_num_ = std::stoul(nvout);
|
|
||||||
|
|
||||||
ins.push_back(utxo);
|
std::string reply_str = fc::to_hex((char*)&unsigned_tx[0], unsigned_tx.size());
|
||||||
|
|
||||||
outs[pw_address] = transfer_amount;
|
|
||||||
|
|
||||||
std::string reply_str = bitcoin_client->createrawtransaction(ins, outs);
|
|
||||||
return sign_and_send_transaction_with_wallet(reply_str);
|
return sign_and_send_transaction_with_wallet(reply_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -808,13 +811,7 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string pw_address_json = obj->addresses.find(sidechain_type::bitcoin)->second;
|
std::string pw_address = obj->addresses.find(sidechain_type::bitcoin)->second;
|
||||||
|
|
||||||
std::stringstream ss(pw_address_json);
|
|
||||||
boost::property_tree::ptree json;
|
|
||||||
boost::property_tree::read_json(ss, json);
|
|
||||||
|
|
||||||
std::string pw_address = json.get<std::string>("address");
|
|
||||||
|
|
||||||
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
|
uint64_t fee_rate = bitcoin_client->estimatesmartfee();
|
||||||
uint64_t min_fee_rate = 1000;
|
uint64_t min_fee_rate = 1000;
|
||||||
|
|
@ -838,13 +835,22 @@ std::string sidechain_net_handler_bitcoin::transfer_withdrawal_from_primary_wall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fc::flat_map<std::string, double> outs;
|
btc_tx tx;
|
||||||
outs[swwo.withdraw_address] = swwo.withdraw_amount.value / 100000000.0;
|
tx.nVersion = 2;
|
||||||
if ((total_amount - min_amount) > 0.0) {
|
tx.nLockTime = 0;
|
||||||
outs[pw_address] = total_amount - min_amount;
|
tx.hasWitness = true;
|
||||||
|
for(const auto& utxo: unspent_utxo)
|
||||||
|
tx.vin.push_back(btc_in(utxo.txid_, utxo.amount_));
|
||||||
|
tx.vout.push_back(btc_out(swwo.withdraw_address, swwo.withdraw_amount.value));
|
||||||
|
if((total_amount - min_amount) > 0.0)
|
||||||
|
{
|
||||||
|
tx.vout.push_back(btc_out(pw_address, (total_amount - min_amount) * 100000000.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string reply_str = bitcoin_client->createrawtransaction(unspent_utxo, outs);
|
bytes unsigned_tx;
|
||||||
|
tx.to_bytes(unsigned_tx);
|
||||||
|
|
||||||
|
std::string reply_str = fc::to_hex((char*)&unsigned_tx[0], unsigned_tx.size());
|
||||||
return sign_and_send_transaction_with_wallet(reply_str);
|
return sign_and_send_transaction_with_wallet(reply_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue