mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Add pegin and pegout RPC calls
This commit is contained in:
parent
7bb9380260
commit
b56f7da6d6
8 changed files with 523 additions and 22 deletions
|
|
@ -142,6 +142,7 @@ BITCOIN_CORE_H = \
|
|||
netmessagemaker.h \
|
||||
noui.h \
|
||||
outputtype.h \
|
||||
pegins.h \
|
||||
policy/feerate.h \
|
||||
policy/fees.h \
|
||||
policy/policy.h \
|
||||
|
|
@ -240,6 +241,7 @@ libbitcoin_server_a_SOURCES = \
|
|||
net_processing.cpp \
|
||||
noui.cpp \
|
||||
outputtype.cpp \
|
||||
pegins.cpp \
|
||||
policy/fees.cpp \
|
||||
policy/policy.cpp \
|
||||
policy/rbf.cpp \
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
#include <consensus/tx_verify.h>
|
||||
|
||||
#include <consensus/consensus.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <pegins.h>
|
||||
#include <primitives/transaction.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <consensus/validation.h>
|
||||
#include <validation.h>
|
||||
|
||||
// TODO remove the following dependencies
|
||||
#include <chain.h>
|
||||
|
|
|
|||
|
|
@ -151,30 +151,50 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
|
|||
}
|
||||
}
|
||||
|
||||
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
UniValue& out, bool fIncludeHex)
|
||||
// ELEMENTS:
|
||||
static void SidechainScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex, bool is_parent_chain)
|
||||
{
|
||||
const std::string prefix = is_parent_chain ? "pegout_" : "";
|
||||
txnouttype type;
|
||||
std::vector<CTxDestination> addresses;
|
||||
int nRequired;
|
||||
|
||||
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
|
||||
out.pushKV(prefix + "asm", ScriptToAsmStr(scriptPubKey));
|
||||
if (fIncludeHex)
|
||||
out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||
out.pushKV(prefix + "hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
|
||||
|
||||
if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) {
|
||||
out.pushKV("type", GetTxnOutputType(type));
|
||||
out.pushKV(prefix + "type", GetTxnOutputType(type));
|
||||
return;
|
||||
}
|
||||
|
||||
out.pushKV("reqSigs", nRequired);
|
||||
out.pushKV("type", GetTxnOutputType(type));
|
||||
out.pushKV(prefix + "reqSigs", nRequired);
|
||||
out.pushKV(prefix + "type", GetTxnOutputType(type));
|
||||
|
||||
UniValue a(UniValue::VARR);
|
||||
for (const CTxDestination& addr : addresses) {
|
||||
a.push_back(EncodeDestination(addr));
|
||||
if (is_parent_chain) {
|
||||
for (const CTxDestination& addr : addresses) {
|
||||
a.push_back(EncodeParentDestination(addr));
|
||||
}
|
||||
} else {
|
||||
for (const CTxDestination& addr : addresses) {
|
||||
a.push_back(EncodeDestination(addr));
|
||||
}
|
||||
}
|
||||
out.pushKV(prefix + "addresses", a);
|
||||
}
|
||||
|
||||
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
|
||||
UniValue& out, bool fIncludeHex)
|
||||
{
|
||||
SidechainScriptPubKeyToJSON(scriptPubKey, out, fIncludeHex, false);
|
||||
|
||||
uint256 pegout_chain;
|
||||
CScript pegout_scriptpubkey;
|
||||
if (scriptPubKey.IsPegoutScript(pegout_chain, pegout_scriptpubkey)) {
|
||||
out.pushKV("pegout_chain", pegout_chain.GetHex());
|
||||
SidechainScriptPubKeyToJSON(pegout_scriptpubkey, out, fIncludeHex, true);
|
||||
}
|
||||
out.pushKV("addresses", a);
|
||||
}
|
||||
|
||||
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <consensus/validation.h>
|
||||
#include <validation.h>
|
||||
#include <coins.h>
|
||||
#include <pegins.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <net.h>
|
||||
#include <netbase.h>
|
||||
#include <outputtype.h>
|
||||
#include <pegins.h>
|
||||
#include <rpc/blockchain.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/util.h>
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@
|
|||
#include <core_io.h>
|
||||
#include <index/txindex.h>
|
||||
#include <keystore.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
#include <key_io.h>
|
||||
#include <merkleblock.h>
|
||||
#include <net.h>
|
||||
#include <pegins.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <primitives/transaction.h>
|
||||
|
|
@ -27,6 +26,8 @@
|
|||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <utilstrencodings.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
#ifdef ENABLE_WALLET
|
||||
#include <wallet/rpcwallet.h>
|
||||
#endif
|
||||
|
|
@ -124,7 +125,12 @@ static UniValue getrawtransaction(const JSONRPCRequest& request)
|
|||
" \"addresses\" : [ (json array of string)\n"
|
||||
" \"address\" (string) bitcoin address\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" ],\n"
|
||||
" \"pegout_chain\" : \"hex\", (string) (only pegout) Hash of genesis block of parent chain'\n"
|
||||
" \"pegout_asm\":\"asm\", (string) (only pegout) pegout scriptpubkey (asm)'\n"
|
||||
" \"pegout_hex\":\"hex\", (string) (only pegout) pegout scriptpubkey (hex)'\n"
|
||||
" \"pegout_type\" : \"pubkeyhash\", (string) (only pegout) The pegout type, eg 'pubkeyhash'\n"
|
||||
" \"pegout_addresses\" : [ (json array of string) (only pegout)\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" ,...\n"
|
||||
|
|
@ -866,19 +872,34 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
|
|||
// Script verification errors
|
||||
UniValue vErrors(UniValue::VARR);
|
||||
|
||||
// ELEMENTS:
|
||||
// Track an immature peg-in that's otherwise valid, give warning
|
||||
bool immature_pegin = false;
|
||||
|
||||
// Use CTransaction for the constant parts of the
|
||||
// transaction to avoid rehashing.
|
||||
const CTransaction txConst(mtx);
|
||||
// Sign what we can:
|
||||
// Sign what we can, including pegin inputs:
|
||||
for (unsigned int i = 0; i < mtx.vin.size(); i++) {
|
||||
CTxIn& txin = mtx.vin[i];
|
||||
const Coin& coin = view.AccessCoin(txin.prevout);
|
||||
if (coin.IsSpent()) {
|
||||
|
||||
if (!txin.m_is_pegin && coin.IsSpent()) {
|
||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
||||
continue;
|
||||
} else if (txin.m_is_pegin && (!IsValidPeginWitness(txConst.vin[i].m_pegin_witness, txin.prevout, false))) {
|
||||
TxInErrorToJSON(txin, vErrors, "Peg-in input has invalid proof.");
|
||||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coin.out.scriptPubKey;
|
||||
const CAmount& amount = coin.out.nValue;
|
||||
// Report warning about immature peg-in though
|
||||
if(txin.m_is_pegin && !IsValidPeginWitness(txConst.vin[i].m_pegin_witness, txin.prevout, true)) {
|
||||
immature_pegin = true;
|
||||
}
|
||||
|
||||
const CScript& prevPubKey = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.vin[i].m_pegin_witness).scriptPubKey : coin.out.scriptPubKey;
|
||||
//TODO(rebase) CT
|
||||
//const CConfidentialValue& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.vin[i].m_pegin_witness).nValue : coin.out.nValue;
|
||||
const CAmount& amount = txin.m_is_pegin ? GetPeginOutputFromWitness(txConst.vin[i].m_pegin_witness).nValue : coin.out.nValue;
|
||||
|
||||
SignatureData sigdata = DataFromTransaction(mtx, i, coin.out);
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
|
|
@ -911,6 +932,9 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival
|
|||
if (!vErrors.empty()) {
|
||||
result.pushKV("errors", vErrors);
|
||||
}
|
||||
if (immature_pegin) {
|
||||
result.pushKV("warning", "Possibly immature peg-in input(s) detected, signed anyways.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -966,6 +990,7 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
|
|||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" \"warning\" : \"text\" (string) Warning that a peg-in input signed may be immature. This could mean lack of connectivity to or misconfiguration of the bitcoind."
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
|
|
@ -1052,6 +1077,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
|||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" \"warning\" : \"text\" (string) Warning that a peg-in input signed may be immature. This could mean lack of connectivity to or misconfiguration of the bitcoind."
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
|
|
@ -1094,7 +1120,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
|||
}
|
||||
}
|
||||
|
||||
static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
|
||||
throw std::runtime_error(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <hash.h>
|
||||
#include <index/txindex.h>
|
||||
#include <mainchainrpc.h>
|
||||
#include <pegins.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
|
|
@ -27,7 +28,6 @@
|
|||
#include <reverse_iterator.h>
|
||||
#include <script/script.h>
|
||||
#include <script/sigcache.h>
|
||||
#include <script/standard.h>
|
||||
#include <shutdown.h>
|
||||
#include <timedata.h>
|
||||
#include <tinyformat.h>
|
||||
|
|
@ -35,7 +35,6 @@
|
|||
#include <txmempool.h>
|
||||
#include <ui_interface.h>
|
||||
#include <undo.h>
|
||||
#include <util.h>
|
||||
#include <utilmoneystr.h>
|
||||
#include <utilstrencodings.h>
|
||||
#include <validationinterface.h>
|
||||
|
|
|
|||
|
|
@ -10,23 +10,30 @@
|
|||
#include <httpserver.h>
|
||||
#include <validation.h>
|
||||
#include <key_io.h>
|
||||
#include <mainchainrpc.h>
|
||||
#include <net.h>
|
||||
#include <outputtype.h>
|
||||
#include <pegins.h>
|
||||
#include <policy/feerate.h>
|
||||
#include <policy/fees.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <pow.h>
|
||||
#include <primitives/bitcoin/merkleblock.h>
|
||||
#include <primitives/bitcoin/transaction.h>
|
||||
#include <rpc/mining.h>
|
||||
#include <rpc/rawtransaction.h>
|
||||
#include <rpc/server.h>
|
||||
#include <rpc/util.h>
|
||||
#include <script/sign.h>
|
||||
#include <secp256k1.h>
|
||||
#include <shutdown.h>
|
||||
#include <timedata.h>
|
||||
#include <util.h>
|
||||
#include <utilmoneystr.h>
|
||||
#include <wallet/coincontrol.h>
|
||||
#include <wallet/feebumper.h>
|
||||
#include <wallet/fees.h>
|
||||
#include <wallet/rpcwallet.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <wallet/walletdb.h>
|
||||
|
|
@ -3711,6 +3718,7 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
|
|||
" }\n"
|
||||
" ,...\n"
|
||||
" ]\n"
|
||||
" \"warning\" : \"text\" (string) Warning that a peg-in input signed may be immature. This could mean lack of connectivity to or misconfiguration of the bitcoind."
|
||||
"}\n"
|
||||
|
||||
"\nExamples:\n"
|
||||
|
|
@ -4828,6 +4836,444 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
|
|||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// ELEMENTS commands
|
||||
|
||||
namespace {
|
||||
static secp256k1_context *secp256k1_ctx;
|
||||
|
||||
class CSecp256k1Init {
|
||||
public:
|
||||
CSecp256k1Init() {
|
||||
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
}
|
||||
~CSecp256k1Init() {
|
||||
secp256k1_context_destroy(secp256k1_ctx);
|
||||
}
|
||||
};
|
||||
static CSecp256k1Init instance_of_csecp256k1;
|
||||
}
|
||||
|
||||
UniValue getpeginaddress(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (request.fHelp || request.params.size() != 0)
|
||||
throw std::runtime_error(
|
||||
"getpeginaddress\n"
|
||||
"\nReturns information needed for claimpegin to move coins to the sidechain.\n"
|
||||
"The user should send coins from their Bitcoin wallet to the mainchain_address returned.\n"
|
||||
"IMPORTANT: Like getaddress, getpeginaddress adds new secrets to wallet.dat, necessitating backup on a regular basis.\n"
|
||||
|
||||
"\nResult:\n"
|
||||
"\"mainchain_address\" (string) Mainchain Bitcoin deposit address to send bitcoin to\n"
|
||||
"\"claim_script\" (string) The claim script in hex that was committed to. This may be required in `claimpegin` to retrieve pegged-in funds\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getpeginaddress", "")
|
||||
+ HelpExampleRpc("getpeginaddress", "")
|
||||
);
|
||||
|
||||
//Creates new address for receiving unlocked utxos
|
||||
JSONRPCRequest req;
|
||||
//TODO(rebase) replace with CT/CA
|
||||
//CTxDestination dest(DecodeDestination(getnewaddress(req).get_str()).GetUnblinded());
|
||||
CTxDestination address = DecodeDestination(getnewaddress(req).get_str());
|
||||
|
||||
Witnessifier w(pwallet);
|
||||
bool ret = boost::apply_visitor(w, address);
|
||||
if (!ret) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
|
||||
}
|
||||
|
||||
pwallet->SetAddressBook(w.result, "", "receive");
|
||||
|
||||
CScript destScript = GetScriptForDestination(address);
|
||||
CScript witProg = GetScriptForWitness(destScript);
|
||||
|
||||
// Also add raw scripts to index to recognize later.
|
||||
pwallet->AddCScript(witProg);
|
||||
pwallet->AddCScript(destScript);
|
||||
|
||||
//Call contracthashtool, get deposit address on mainchain.
|
||||
CTxDestination destAddr(CScriptID(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witProg))));
|
||||
|
||||
UniValue fundinginfo(UniValue::VOBJ);
|
||||
|
||||
fundinginfo.pushKV("mainchain_address", EncodeParentDestination(destAddr));
|
||||
fundinginfo.pushKV("claim_script", HexStr(witProg));
|
||||
return fundinginfo;
|
||||
}
|
||||
|
||||
UniValue sendtomainchain(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
if (!EnsureWalletIsAvailable(pwallet, request.fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
"sendtomainchain mainchainaddress amount ( subtractfeefromamount )\n"
|
||||
"\nSends sidechain funds to the given mainchain address, through the federated pegin mechanism\n"
|
||||
+ HelpRequiringPassphrase(pwallet) +
|
||||
"\nArguments:\n"
|
||||
"1. \"address\" (string, required) The destination address on Bitcoin mainchain\n"
|
||||
"2. \"amount\" (numeric, required) The amount being sent to Bitcoin mainchain\n"
|
||||
"3. \"subtractfeefromamount\" (boolean, optional, default=false) The fee will be deducted from the amount being pegged-out.\n"
|
||||
"\nResult:\n"
|
||||
"\"txid\" (string) Transaction ID of the resulting sidechain transaction\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("sendtomainchain", "\"mgWEy4vBJSHt3mC8C2SEWJQitifb4qeZQq\" 0.1")
|
||||
+ HelpExampleRpc("sendtomainchain", "\"mgWEy4vBJSHt3mC8C2SEWJQitifb4qeZQq\" 0.1")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
CTxDestination parent_address = DecodeDestination(request.params[0].get_str());
|
||||
if (!IsValidDestination(parent_address))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
||||
|
||||
CAmount nAmount = AmountFromValue(request.params[1]);
|
||||
if (nAmount <= 0)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
|
||||
|
||||
bool subtract_fee = false;
|
||||
if (request.params.size() > 2) {
|
||||
subtract_fee = request.params[2].get_bool();
|
||||
}
|
||||
|
||||
// Parse Bitcoin address for destination, embed script
|
||||
CScript scriptPubKeyMainchain(GetScriptForDestination(parent_address));
|
||||
|
||||
uint256 genesisBlockHash = Params().ParentGenesisBlockHash();
|
||||
|
||||
// Asset type is implicit, no need to add to script
|
||||
NullData nulldata;
|
||||
nulldata << std::vector<unsigned char>(genesisBlockHash.begin(), genesisBlockHash.end());
|
||||
nulldata << std::vector<unsigned char>(scriptPubKeyMainchain.begin(), scriptPubKeyMainchain.end());
|
||||
CTxDestination address(nulldata);
|
||||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
mapValue_t mapValue;
|
||||
CCoinControl no_coin_control; // This is a deprecated API
|
||||
CTransactionRef tx = SendMoney(pwallet, address, nAmount, subtract_fee, no_coin_control, std::move(mapValue), {});
|
||||
|
||||
//TODO(rebase) CT/CA
|
||||
// v this line was in elements-0.14 instead of the line above here
|
||||
////SendMoney(scriptPubKey, nAmount, Params().GetConsensus().pegged_asset, subtract_fee, CPubKey(), wtxNew, true);
|
||||
|
||||
return (*tx).GetHash().GetHex();
|
||||
}
|
||||
|
||||
extern UniValue signrawtransaction(const JSONRPCRequest& request);
|
||||
extern UniValue sendrawtransaction(const JSONRPCRequest& request);
|
||||
|
||||
template<typename T_tx>
|
||||
unsigned int GetPeginTxnOutputIndex(const T_tx& txn, const CScript& witnessProgram)
|
||||
{
|
||||
unsigned int nOut = 0;
|
||||
//Call contracthashtool
|
||||
CScript mainchain_script = GetScriptForDestination(CScriptID(GetScriptForWitness(calculate_contract(Params().GetConsensus().fedpegScript, witnessProgram))));
|
||||
for (; nOut < txn.vout.size(); nOut++)
|
||||
if (txn.vout[nOut].scriptPubKey == mainchain_script)
|
||||
break;
|
||||
return nOut;
|
||||
}
|
||||
|
||||
template<typename T_tx_ref, typename T_tx, typename T_merkle_block>
|
||||
static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef, T_tx& tx_aux, T_merkle_block& merkleBlock)
|
||||
{
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
"createrawpegin bitcoinTx txoutproof ( claim_script )\n"
|
||||
"\nCreates a raw transaction to claim coins from the main chain by creating a pegin transaction with the necessary metadata after the corresponding Bitcoin transaction.\n"
|
||||
"Note that this call will not sign the transaction.\n"
|
||||
"If a transaction is not relayed it may require manual addition to a functionary mempool in order for it to be mined.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"bitcoinTx\" (string, required) The raw bitcoin transaction (in hex) depositing bitcoin to the mainchain_address generated by getpeginaddress\n"
|
||||
"2. \"txoutproof\" (string, required) A rawtxoutproof (in hex) generated by bitcoind's `gettxoutproof` containing a proof of only bitcoinTx\n"
|
||||
"3. \"claim_script\" (string, optional) The witness program generated by getpeginaddress. Only needed if not in wallet.\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"hex\" (string) Raw transaction in hex\n"
|
||||
" \"mature\" (bool) Whether the peg-in is mature (only included when validating peg-ins)\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("createrawpegin", "\"0200000002b80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f000000006a473044022031ffe1d76decdfbbdb7e2ee6010e865a5134137c261e1921da0348b95a207f9e02203596b065c197e31bcc2f80575154774ac4e80acd7d812c91d93c4ca6a3636f27012102d2130dfbbae9bd27eee126182a39878ac4e117d0850f04db0326981f43447f9efeffffffb80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f010000006b483045022100cf041ce0eb249ae5a6bc33c71c156549c7e5ad877ae39e2e3b9c8f1d81ed35060220472d4e4bcc3b7c8d1b34e467f46d80480959183d743dad73b1ed0e93ec9fd14f012103e73e8b55478ab9c5de22e2a9e73c3e6aca2c2e93cd2bad5dc4436a9a455a5c44feffffff0200e1f5050000000017a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87e86cbe00000000001976a914a25fe72e7139fd3f61936b228d657b2548b3936a88acc0020000\", \"00000020976e918ed537b0f99028648f2a25c0bd4513644fb84d9cbe1108b4df6b8edf6ba715c424110f0934265bf8c5763d9cc9f1675a0f728b35b9bc5875f6806be3d19cd5b159ffff7f2000000000020000000224eab3da09d99407cb79f0089e3257414c4121cb85a320e1fd0f88678b6b798e0713a8d66544b6f631f9b6d281c71633fb91a67619b189a06bab09794d5554a60105\" \"0014058c769ffc7d12c35cddec87384506f536383f9c\"")
|
||||
+ HelpExampleRpc("createrawpegin", "\"0200000002b80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f000000006a473044022031ffe1d76decdfbbdb7e2ee6010e865a5134137c261e1921da0348b95a207f9e02203596b065c197e31bcc2f80575154774ac4e80acd7d812c91d93c4ca6a3636f27012102d2130dfbbae9bd27eee126182a39878ac4e117d0850f04db0326981f43447f9efeffffffb80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f010000006b483045022100cf041ce0eb249ae5a6bc33c71c156549c7e5ad877ae39e2e3b9c8f1d81ed35060220472d4e4bcc3b7c8d1b34e467f46d80480959183d743dad73b1ed0e93ec9fd14f012103e73e8b55478ab9c5de22e2a9e73c3e6aca2c2e93cd2bad5dc4436a9a455a5c44feffffff0200e1f5050000000017a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87e86cbe00000000001976a914a25fe72e7139fd3f61936b228d657b2548b3936a88acc0020000\", \"00000020976e918ed537b0f99028648f2a25c0bd4513644fb84d9cbe1108b4df6b8edf6ba715c424110f0934265bf8c5763d9cc9f1675a0f728b35b9bc5875f6806be3d19cd5b159ffff7f2000000000020000000224eab3da09d99407cb79f0089e3257414c4121cb85a320e1fd0f88678b6b798e0713a8d66544b6f631f9b6d281c71633fb91a67619b189a06bab09794d5554a60105\", \"0014058c769ffc7d12c35cddec87384506f536383f9c\"")
|
||||
);
|
||||
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
if (!IsHex(request.params[0].get_str()) || !IsHex(request.params[1].get_str())) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "the first two arguments must be hex strings");
|
||||
}
|
||||
|
||||
std::vector<unsigned char> txData = ParseHex(request.params[0].get_str());
|
||||
CDataStream ssTx(txData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
try {
|
||||
ssTx >> txBTCRef;
|
||||
}
|
||||
catch (...) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "The included bitcoinTx is malformed. Are you sure that is the whole string?");
|
||||
}
|
||||
T_tx txBTC(*txBTCRef);
|
||||
|
||||
std::vector<unsigned char> txOutProofData = ParseHex(request.params[1].get_str());
|
||||
CDataStream ssTxOutProof(txOutProofData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
try {
|
||||
ssTxOutProof >> merkleBlock;
|
||||
}
|
||||
catch (...) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "The included txoutproof is malformed. Are you sure that is the whole string?");
|
||||
}
|
||||
|
||||
if (!ssTxOutProof.empty()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
|
||||
}
|
||||
|
||||
std::vector<uint256> txHashes;
|
||||
std::vector<unsigned int> txIndices;
|
||||
if (merkleBlock.txn.ExtractMatches(txHashes, txIndices) != merkleBlock.header.hashMerkleRoot)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
|
||||
|
||||
if (txHashes.size() != 1 || txHashes[0] != txBTC.GetHash())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "The txoutproof must contain bitcoinTx and only bitcoinTx");
|
||||
|
||||
CScript witnessProgScript;
|
||||
unsigned int nOut = txBTC.vout.size();
|
||||
if (request.params.size() > 2) {
|
||||
const std::string claim_script = request.params[2].get_str();
|
||||
if (!IsHex(claim_script)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given claim_script is not hex.");
|
||||
}
|
||||
// If given manually, no need for it to be a witness script
|
||||
std::vector<unsigned char> witnessBytes(ParseHex(claim_script));
|
||||
witnessProgScript = CScript(witnessBytes.begin(), witnessBytes.end());
|
||||
nOut = GetPeginTxnOutputIndex(txBTC, witnessProgScript);
|
||||
if (nOut == txBTC.vout.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given claim_script does not match the given Bitcoin transaction.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Look through address book for pegin contract value by extracting the unlderlying witness program from p2sh-p2wpkh
|
||||
for (std::map<CTxDestination, CAddressBookData>::const_iterator iter = pwallet->mapAddressBook.begin(); iter != pwallet->mapAddressBook.end(); ++iter) {
|
||||
CTxDestination sidechainAddress(CTxDestination(iter->first));
|
||||
CScript witnessProgramScript = GetScriptForWitness(GetScriptForDestination(sidechainAddress));
|
||||
int version;
|
||||
std::vector<unsigned char> witnessProgram;
|
||||
// Only process witness v0 programs
|
||||
if (!witnessProgramScript.IsWitnessProgram(version, witnessProgram) || version != 0) {
|
||||
continue;
|
||||
}
|
||||
nOut = GetPeginTxnOutputIndex(txBTC, witnessProgramScript);
|
||||
if (nOut != txBTC.vout.size()) {
|
||||
witnessProgScript = witnessProgramScript;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nOut == txBTC.vout.size()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Failed to find output in bitcoinTx to the mainchain_address from getpeginaddress");
|
||||
}
|
||||
assert(witnessProgScript != CScript());
|
||||
|
||||
int version = -1;
|
||||
std::vector<unsigned char> witnessProgram;
|
||||
if (!witnessProgScript.IsWitnessProgram(version, witnessProgram)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given or recovered script is not a witness program.");
|
||||
}
|
||||
|
||||
CAmount value = 0;
|
||||
if (!GetAmountFromParentChainPegin(value, txBTC, nOut)) {
|
||||
//TODO(rebase) CT/CA
|
||||
//throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Amounts to pegin must be explicit and asset must be %s", Params().GetConsensus().parent_pegged_asset.GetHex()));
|
||||
}
|
||||
|
||||
CDataStream stream(0, 0);
|
||||
try {
|
||||
stream << value;
|
||||
} catch (...) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Amount serialization is invalid.");
|
||||
}
|
||||
// Need to reinterpret bytes as unsigned chars before adding to witness
|
||||
char* buf = stream.data();
|
||||
unsigned char* membuf = reinterpret_cast<unsigned char*>(buf);
|
||||
std::vector<unsigned char> value_bytes(membuf, membuf + stream.size());
|
||||
|
||||
uint256 genesisBlockHash = Params().ParentGenesisBlockHash();
|
||||
|
||||
// Manually construct peg-in transaction, sign it, and send it off.
|
||||
// Decrement the output value as much as needed given the total vsize to
|
||||
// pay the fees.
|
||||
|
||||
if (!pwallet->IsLocked())
|
||||
pwallet->TopUpKeyPool();
|
||||
|
||||
// Generate a new key that is added to wallet
|
||||
CPubKey newKey;
|
||||
if (!pwallet->GetKeyFromPool(newKey))
|
||||
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
|
||||
CKeyID keyID = newKey.GetID();
|
||||
|
||||
pwallet->SetAddressBook(keyID, "", "receive");
|
||||
|
||||
// One peg-in input, one wallet output and one fee output
|
||||
CMutableTransaction mtx;
|
||||
mtx.vin.push_back(CTxIn(COutPoint(txHashes[0], nOut), CScript(), ~(uint32_t)0));
|
||||
// mark as peg-in input
|
||||
mtx.vin[0].m_is_pegin = true;
|
||||
//TODO(rebase) CA
|
||||
//mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, value, GetScriptForDestination(CTxDestination(keyID))));
|
||||
//mtx.vout.push_back(CTxOut(Params().GetConsensus().pegged_asset, 0, CScript()));
|
||||
mtx.vout.push_back(CTxOut(value, GetScriptForDestination(CTxDestination(keyID))));
|
||||
|
||||
// Construct pegin proof
|
||||
CScriptWitness pegin_witness;
|
||||
std::vector<std::vector<unsigned char> >& stack = pegin_witness.stack;
|
||||
stack.push_back(value_bytes);
|
||||
//TODO(rebase) CA
|
||||
std::vector<unsigned char> empty(32);
|
||||
stack.push_back(empty);
|
||||
//stack.push_back(std::vector<unsigned char>(Params().GetConsensus().pegged_asset.begin(), Params().GetConsensus().pegged_asset.end()));
|
||||
stack.push_back(std::vector<unsigned char>(genesisBlockHash.begin(), genesisBlockHash.end()));
|
||||
stack.push_back(std::vector<unsigned char>(witnessProgScript.begin(), witnessProgScript.end()));
|
||||
stack.push_back(txData);
|
||||
stack.push_back(txOutProofData);
|
||||
|
||||
// Peg-in witness isn't valid, even though the block header is(without depth check)
|
||||
// We re-check depth before returning with more descriptive result
|
||||
if (!IsValidPeginWitness(pegin_witness, mtx.vin[0].prevout, false)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Constructed peg-in witness is invalid.");
|
||||
}
|
||||
|
||||
// Put input witness in transaction
|
||||
mtx.vin[0].m_pegin_witness = pegin_witness;
|
||||
|
||||
// Estimate fee for transaction, decrement fee output(including witness data)
|
||||
unsigned int nBytes = GetVirtualTransactionSize(mtx) +
|
||||
(1+1+72+1+33/WITNESS_SCALE_FACTOR);
|
||||
CCoinControl coin_control;
|
||||
CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, mempool, ::feeEstimator, nullptr);
|
||||
|
||||
//TODO(rebase) CT
|
||||
//mtx.vout[0].nValue = mtx.vout[0].nValue.GetAmount() - nFeeNeeded;
|
||||
//mtx.vout[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded;
|
||||
mtx.vout[0].nValue = mtx.vout[0].nValue - nFeeNeeded;
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
// Return hex
|
||||
std::string strHex = EncodeHexTx(mtx, RPCSerializationFlags());
|
||||
ret.pushKV("hex", strHex);
|
||||
|
||||
// Additional block lee-way to avoid bitcoin block races
|
||||
if (gArgs.GetBoolArg("-validatepegin", DEFAULT_VALIDATE_PEGIN)) {
|
||||
ret.pushKV("mature", IsConfirmedBitcoinBlock(merkleBlock.header.GetHash(), Params().GetConsensus().pegin_min_depth+2, merkleBlock.txn.GetNumTransactions()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue createrawpegin(const JSONRPCRequest& request)
|
||||
{
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
if (Params().GetConsensus().ParentChainHasPow()) {
|
||||
Sidechain::Bitcoin::CTransactionRef txBTCRef;
|
||||
Sidechain::Bitcoin::CTransaction tx_aux;
|
||||
Sidechain::Bitcoin::CMerkleBlock merkleBlock;
|
||||
ret = createrawpegin(request, txBTCRef, tx_aux, merkleBlock);
|
||||
if (!CheckParentProofOfWork(merkleBlock.header.GetHash(), merkleBlock.header.nBits, Params().GetConsensus())) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
|
||||
}
|
||||
} else {
|
||||
//TODO(rebase) parent signed blocks
|
||||
//CTransactionRef txBTCRef;
|
||||
//CTransaction tx_aux;
|
||||
//CMerkleBlock merkleBlock;
|
||||
//ret = createrawpegin(request, txBTCRef, tx_aux, merkleBlock);
|
||||
//if (!CheckProofSignedParent(merkleBlock.header, Params().GetConsensus())) {
|
||||
// throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid tx out proof");
|
||||
//}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
UniValue claimpegin(const JSONRPCRequest& request)
|
||||
{
|
||||
|
||||
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
|
||||
throw std::runtime_error(
|
||||
"claimpegin bitcoinTx txoutproof ( claim_script )\n"
|
||||
"\nClaim coins from the main chain by creating a pegin transaction with the necessary metadata after the corresponding Bitcoin transaction.\n"
|
||||
"Note that the transaction will not be relayed unless it is buried at least 102 blocks deep.\n"
|
||||
"If a transaction is not relayed it may require manual addition to a functionary mempool in order for it to be mined.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"bitcoinTx\" (string, required) The raw bitcoin transaction (in hex) depositing bitcoin to the mainchain_address generated by getpeginaddress\n"
|
||||
"2. \"txoutproof\" (string, required) A rawtxoutproof (in hex) generated by bitcoind's `gettxoutproof` containing a proof of only bitcoinTx\n"
|
||||
"3. \"claim_script\" (string, optional) The witness program generated by getpeginaddress. Only needed if not in wallet.\n"
|
||||
"\nResult:\n"
|
||||
"\"txid\" (string) Txid of the resulting sidechain transaction\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("claimpegin", "\"0200000002b80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f000000006a473044022031ffe1d76decdfbbdb7e2ee6010e865a5134137c261e1921da0348b95a207f9e02203596b065c197e31bcc2f80575154774ac4e80acd7d812c91d93c4ca6a3636f27012102d2130dfbbae9bd27eee126182a39878ac4e117d0850f04db0326981f43447f9efeffffffb80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f010000006b483045022100cf041ce0eb249ae5a6bc33c71c156549c7e5ad877ae39e2e3b9c8f1d81ed35060220472d4e4bcc3b7c8d1b34e467f46d80480959183d743dad73b1ed0e93ec9fd14f012103e73e8b55478ab9c5de22e2a9e73c3e6aca2c2e93cd2bad5dc4436a9a455a5c44feffffff0200e1f5050000000017a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87e86cbe00000000001976a914a25fe72e7139fd3f61936b228d657b2548b3936a88acc0020000\", \"00000020976e918ed537b0f99028648f2a25c0bd4513644fb84d9cbe1108b4df6b8edf6ba715c424110f0934265bf8c5763d9cc9f1675a0f728b35b9bc5875f6806be3d19cd5b159ffff7f2000000000020000000224eab3da09d99407cb79f0089e3257414c4121cb85a320e1fd0f88678b6b798e0713a8d66544b6f631f9b6d281c71633fb91a67619b189a06bab09794d5554a60105\" \"0014058c769ffc7d12c35cddec87384506f536383f9c\"")
|
||||
+ HelpExampleRpc("claimpegin", "\"0200000002b80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f000000006a473044022031ffe1d76decdfbbdb7e2ee6010e865a5134137c261e1921da0348b95a207f9e02203596b065c197e31bcc2f80575154774ac4e80acd7d812c91d93c4ca6a3636f27012102d2130dfbbae9bd27eee126182a39878ac4e117d0850f04db0326981f43447f9efeffffffb80a99d63ca943d72141750d983a3eeda3a5c5a92aa962884ffb141eb49ffb4f010000006b483045022100cf041ce0eb249ae5a6bc33c71c156549c7e5ad877ae39e2e3b9c8f1d81ed35060220472d4e4bcc3b7c8d1b34e467f46d80480959183d743dad73b1ed0e93ec9fd14f012103e73e8b55478ab9c5de22e2a9e73c3e6aca2c2e93cd2bad5dc4436a9a455a5c44feffffff0200e1f5050000000017a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87e86cbe00000000001976a914a25fe72e7139fd3f61936b228d657b2548b3936a88acc0020000\", \"00000020976e918ed537b0f99028648f2a25c0bd4513644fb84d9cbe1108b4df6b8edf6ba715c424110f0934265bf8c5763d9cc9f1675a0f728b35b9bc5875f6806be3d19cd5b159ffff7f2000000000020000000224eab3da09d99407cb79f0089e3257414c4121cb85a320e1fd0f88678b6b798e0713a8d66544b6f631f9b6d281c71633fb91a67619b189a06bab09794d5554a60105\", \"0014058c769ffc7d12c35cddec87384506f536383f9c\"")
|
||||
);
|
||||
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
CWallet* const pwallet = wallet.get();
|
||||
CTransactionRef tx_ref;
|
||||
CMutableTransaction mtx;
|
||||
|
||||
LOCK2(cs_main, pwallet->cs_wallet);
|
||||
|
||||
if (IsInitialBlockDownload()) {
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Peg-ins cannot be completed during initial sync or reindexing.");
|
||||
}
|
||||
|
||||
// Get raw peg-in transaction
|
||||
UniValue ret(createrawpegin(request));
|
||||
|
||||
// Make sure it can be propagated and confirmed
|
||||
if (!ret["mature"].isNull() && ret["mature"].get_bool() == false) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Peg-in Bitcoin transaction needs more confirmations to be sent.");
|
||||
}
|
||||
|
||||
// Sign it
|
||||
JSONRPCRequest request2;
|
||||
UniValue varr(UniValue::VARR);
|
||||
varr.push_back(ret["hex"]);
|
||||
request2.params = varr;
|
||||
UniValue result = signrawtransactionwithwallet(request2);
|
||||
|
||||
if (!DecodeHexTx(mtx, result["hex"].get_str(), false, true)) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
}
|
||||
|
||||
// Send it
|
||||
CValidationState state;
|
||||
mapValue_t mapValue;
|
||||
CReserveKey reservekey(pwallet);
|
||||
if (!pwallet->CommitTransaction(MakeTransactionRef(mtx), mapValue, {} /* orderForm */, "", reservekey, g_connman.get(), state)) {
|
||||
std::string strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strError);
|
||||
}
|
||||
|
||||
return mtx.GetHash().GetHex();
|
||||
}
|
||||
|
||||
// END ELEMENTS commands
|
||||
//
|
||||
|
||||
extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
|
||||
extern UniValue importprivkey(const JSONRPCRequest& request);
|
||||
|
|
@ -4893,6 +5339,12 @@ static const CRPCCommand commands[] =
|
|||
{ "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} },
|
||||
{ "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} },
|
||||
{ "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} },
|
||||
// ELEMENTS:
|
||||
{ "wallet", "getpeginaddress", &getpeginaddress, {} },
|
||||
{ "wallet", "claimpegin", &claimpegin, {"bitcoin_tx", "txoutproof", "claim_script"} },
|
||||
{ "wallet", "createrawpegin", &createrawpegin, {"bitcoin_tx", "txoutproof", "claim_script"} },
|
||||
{ "wallet", "sendtomainchain", &sendtomainchain, {"address", "amount", "subtractfeefromamount"} },
|
||||
|
||||
|
||||
/** Account functions (deprecated) */
|
||||
{ "wallet", "getaccountaddress", &getaccountaddress, {"account"} },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue