Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <pegins.h>
#include <psbt.h>
#include <util/strencodings.h>
#include <confidential_validation.h>
Expand Down Expand Up @@ -158,6 +159,11 @@ void PSBTInput::Merge(const PSBTInput& input)
if (value_blinding_factor.IsNull() && !input.value_blinding_factor.IsNull()) value_blinding_factor = input.value_blinding_factor;
if (asset.IsNull() && !input.asset.IsNull()) asset = input.asset;
if (asset_blinding_factor.IsNull() && !input.asset_blinding_factor.IsNull()) asset_blinding_factor = input.asset_blinding_factor;

if (peg_in_tx.which() == 0 && peg_in_tx.which() > 0) peg_in_tx = input.peg_in_tx;
if (txout_proof.which() == 0 && peg_in_tx.which() > 0) txout_proof = input.txout_proof;
if (claim_script.empty() && !input.claim_script.empty()) claim_script = input.claim_script;
if (genesis_hash.IsNull() && !input.genesis_hash.IsNull()) genesis_hash = input.genesis_hash;
}

bool PSBTInput::IsSane() const
Expand Down Expand Up @@ -321,6 +327,22 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
for (unsigned int i = 0; i < result.vin.size(); ++i) {
result.vin[i].scriptSig = psbtx.inputs[i].final_script_sig;
result.witness.vtxinwit[i].scriptWitness = psbtx.inputs[i].final_script_witness;
PSBTInput& input = psbtx.inputs[i];

if (input.value && input.peg_in_tx.which() != 0 && input.txout_proof.which() != 0 && !input.claim_script.empty() && !input.genesis_hash.IsNull()) {
CScriptWitness pegin_witness;
if (Params().GetConsensus().ParentChainHasPow()) {
const Sidechain::Bitcoin::CTransactionRef& btc_peg_in_tx = boost::get<Sidechain::Bitcoin::CTransactionRef>(input.peg_in_tx);
const Sidechain::Bitcoin::CMerkleBlock& btc_txout_proof = boost::get<Sidechain::Bitcoin::CMerkleBlock>(input.txout_proof);
pegin_witness = CreatePeginWitness(*input.value, input.asset, input.genesis_hash, input.claim_script, btc_peg_in_tx, btc_txout_proof);
} else {
const CTransactionRef& elem_peg_in_tx = boost::get<CTransactionRef>(input.peg_in_tx);
const CMerkleBlock& elem_txout_proof = boost::get<CMerkleBlock>(input.txout_proof);
pegin_witness = CreatePeginWitness(*input.value, input.asset, input.genesis_hash, input.claim_script, elem_peg_in_tx, elem_txout_proof);
}
result.vin[i].m_is_pegin = true;
result.witness.vtxinwit[i].m_pegin_witness = pegin_witness;
}
}

result.witness.vtxoutwit.resize(result.vout.size());
Expand Down
117 changes: 117 additions & 0 deletions src/psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
#define BITCOIN_PSBT_H

#include <attributes.h>
#include <chainparams.h>
#include <node/transaction.h>
#include <primitives/transaction.h>
#include <primitives/bitcoin/transaction.h>
#include <primitives/bitcoin/merkleblock.h>
#include <merkleblock.h>
#include <pubkey.h>
#include <script/sign.h>

#include <boost/variant.hpp>

// Magic bytes
static constexpr uint8_t PSBT_MAGIC_BYTES[5] = {'p', 's', 'b', 't', 0xff};
static constexpr uint8_t PSBT_ELEMENTS_MAGIC_BYTES[5] = {'p', 's', 'e', 't', 0xff};
Expand All @@ -35,6 +41,11 @@ static constexpr uint8_t PSBT_IN_VALUE = 0x00;
static constexpr uint8_t PSBT_IN_VALUE_BLINDER = 0x01;
static constexpr uint8_t PSBT_IN_ASSET = 0x02;
static constexpr uint8_t PSBT_IN_ASSET_BLINDER = 0x03;
// Peg-in stuff
static constexpr uint8_t PSBT_IN_PEG_IN_TX = 0x04;
static constexpr uint8_t PSBT_IN_TXOUT_PROOF = 0x05;
static constexpr uint8_t PSBT_IN_GENESIS_HASH = 0x06;
static constexpr uint8_t PSBT_IN_CLAIM_SCRIPT = 0x07;

// Output types
static constexpr uint8_t PSBT_OUT_REDEEMSCRIPT = 0x00;
Expand Down Expand Up @@ -77,6 +88,11 @@ struct PSBTInput
CAsset asset;
uint256 asset_blinding_factor;

boost::variant<boost::blank, CTransactionRef, Sidechain::Bitcoin::CTransactionRef> peg_in_tx;
boost::variant<boost::blank, CMerkleBlock, Sidechain::Bitcoin::CMerkleBlock> txout_proof;
CScript claim_script;
uint256 genesis_hash;

bool IsNull() const;
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
Expand Down Expand Up @@ -158,6 +174,49 @@ struct PSBTInput
SerializeToVector(s, final_script_witness.stack);
}

// Write peg-in data
if (Params().GetConsensus().ParentChainHasPow()) {
if (peg_in_tx.which() > 0) {
const Sidechain::Bitcoin::CTransactionRef& btc_peg_in_tx = boost::get<Sidechain::Bitcoin::CTransactionRef>(peg_in_tx);
if (btc_peg_in_tx) {
SerializeToVector(s, PSBT_IN_PROPRIETARY, PSBT_ELEMENTS_ID, PSBT_IN_PEG_IN_TX);
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, btc_peg_in_tx);
}
}
if (txout_proof.which() > 0) {
const Sidechain::Bitcoin::CMerkleBlock& btc_txout_proof = boost::get<Sidechain::Bitcoin::CMerkleBlock>(txout_proof);
if (!btc_txout_proof.header.IsNull()) {
SerializeToVector(s, PSBT_IN_PROPRIETARY, PSBT_ELEMENTS_ID, PSBT_IN_TXOUT_PROOF);
SerializeToVector(s, btc_txout_proof);
}
}
} else {
if (peg_in_tx.which() > 0) {
const CTransactionRef& elem_peg_in_tx = boost::get<CTransactionRef>(peg_in_tx);
if (elem_peg_in_tx) {
SerializeToVector(s, PSBT_IN_PROPRIETARY, PSBT_ELEMENTS_ID, PSBT_IN_PEG_IN_TX);
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
SerializeToVector(os, elem_peg_in_tx);
}
}
if (txout_proof.which() > 0) {
const CMerkleBlock& elem_txout_proof = boost::get<CMerkleBlock>(txout_proof);
if (!elem_txout_proof.header.IsNull()) {
SerializeToVector(s, PSBT_IN_PROPRIETARY, PSBT_ELEMENTS_ID, PSBT_IN_TXOUT_PROOF);
SerializeToVector(s, elem_txout_proof);
}
}
}
if (!claim_script.empty()) {
SerializeToVector(s, PSBT_IN_PROPRIETARY, PSBT_ELEMENTS_ID, PSBT_IN_CLAIM_SCRIPT);
s << claim_script;
}
if (!genesis_hash.IsNull()) {
SerializeToVector(s, PSBT_IN_PROPRIETARY, PSBT_ELEMENTS_ID, PSBT_IN_GENESIS_HASH);
SerializeToVector(s, genesis_hash);
}

// Write unknown things
for (auto& entry : unknown) {
s << entry.first;
Expand Down Expand Up @@ -342,6 +401,64 @@ struct PSBTInput
UnserializeFromVector(s, asset_blinding_factor);
break;
}
case PSBT_IN_PEG_IN_TX:
{
if (peg_in_tx.which() != 0) {
throw std::ios_base::failure("Duplicate Key, peg-in tx already provided");
} else if (subkey_len != 1) {
throw std::ios_base::failure("Peg-in tx key is more than one byte type");
}
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CTransactionRef tx_btc;
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion());
UnserializeFromVector(os, tx_btc);
peg_in_tx = tx_btc;
} else {
CTransactionRef tx_btc;
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion());
UnserializeFromVector(os, tx_btc);
peg_in_tx = tx_btc;
}
break;
}
case PSBT_IN_TXOUT_PROOF:
{
if (txout_proof.which() != 0) {
throw std::ios_base::failure("Duplicate Key, peg-in txout proof already provided");
} else if (subkey_len != 1) {
throw std::ios_base::failure("Peg-in txout proof key is more than one byte type");
}
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CMerkleBlock tx_proof;
UnserializeFromVector(s, tx_proof);
txout_proof = tx_proof;
} else {
CMerkleBlock tx_proof;
UnserializeFromVector(s, tx_proof);
txout_proof = tx_proof;
}
break;
}
case PSBT_IN_CLAIM_SCRIPT:
{
if (!claim_script.empty()) {
throw std::ios_base::failure("Duplicate Key, peg-in claim script already provided");
} else if (subkey_len != 1) {
throw std::ios_base::failure("Peg-in claim script key is more than one byte type");
}
s >> claim_script;
break;
}
case PSBT_IN_GENESIS_HASH:
{
if (!genesis_hash.IsNull()) {
throw std::ios_base::failure("Duplicate Key, peg-in genesis hash already provided");
} else if (subkey_len != 1) {
throw std::ios_base::failure("Peg-in genesis hash is more than one byte type");
}
UnserializeFromVector(s, genesis_hash);
break;
}
}
break;
}
Expand Down
88 changes: 87 additions & 1 deletion src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1660,6 +1660,10 @@ UniValue decodepsbt(const JSONRPCRequest& request)
" \"value_blinding_factor\": \"hex\" , (string) The value blinding factor from the output being spent\n"
" \"asset\": \"hex\" , (string) The (unblinded) asset id of the input\n"
" \"asset_blinding_factor\": \"hex\" , (string) The asset blinding factor from the output being spent\n"
" \"pegin_bitcoin_tx\": \"hex\", (string) The tx providing the peg-in in the format of the getrawtransaction RPC.\n"
" \"pegin_claim_script\": \"hex\", (string) The claim script for the peg-in input\n"
" \"pegin_txout_proof\": \"hex\", (string) The tx providing the peg-in input\n"
" \"pegin_genesis_hash\": \"hex\", (string) The hash of the genesis block for this peg-in\n"
" \"unknown\" : { (json object) The unknown input fields\n"
" \"key\" : \"value\" (key-value pair) An unknown key-value pair\n"
" ...\n"
Expand Down Expand Up @@ -1836,6 +1840,49 @@ UniValue decodepsbt(const JSONRPCRequest& request)
in.pushKV("asset_blinding_factor", input.asset_blinding_factor.GetHex());
}

// Peg-in stuff
if (Params().GetConsensus().ParentChainHasPow()) {
if (input.peg_in_tx.which() > 0) {
const Sidechain::Bitcoin::CTransactionRef& btc_peg_in_tx = boost::get<Sidechain::Bitcoin::CTransactionRef>(input.peg_in_tx);
if (btc_peg_in_tx) {
CDataStream ss_tx(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
ss_tx << btc_peg_in_tx;
in.pushKV("pegin_bitcoin_tx", HexStr(ss_tx.begin(), ss_tx.end()));
}
}
if (input.txout_proof.which() > 0) {
const Sidechain::Bitcoin::CMerkleBlock& btc_txout_proof = boost::get<Sidechain::Bitcoin::CMerkleBlock>(input.txout_proof);
if (!btc_txout_proof.header.IsNull()) {
CDataStream ss_mb(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
ss_mb << btc_txout_proof;
in.pushKV("pegin_txout_proof", HexStr(ss_mb.begin(), ss_mb.end()));
}
}
} else {
if (input.peg_in_tx.which() > 0) {
const CTransactionRef& elem_peg_in_tx = boost::get<CTransactionRef>(input.peg_in_tx);
if (elem_peg_in_tx) {
CDataStream ss_tx(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
ss_tx << elem_peg_in_tx;
in.pushKV("pegin_bitcoin_tx", HexStr(ss_tx.begin(), ss_tx.end()));
}
}
if (input.txout_proof.which() > 0) {
const CMerkleBlock& elem_txout_proof = boost::get<CMerkleBlock>(input.txout_proof);
if (!elem_txout_proof.header.IsNull()) {
CDataStream ss_mb(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
ss_mb << elem_txout_proof;
in.pushKV("pegin_txout_proof", HexStr(ss_mb.begin(), ss_mb.end()));
}
}
}
if (!input.claim_script.empty()) {
in.pushKV("pegin_claim_script", HexStr(input.claim_script.begin(), input.claim_script.end()));
}
if (!input.genesis_hash.IsNull()) {
in.pushKV("pegin_genesis_hash", input.genesis_hash.GetHex());
}

// Unknown data
if (input.unknown.size() > 0) {
UniValue unknowns(UniValue::VOBJ);
Expand Down Expand Up @@ -2062,6 +2109,9 @@ UniValue createpsbt(const JSONRPCRequest& request)
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
{"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'replaceable' and 'locktime' arguments", "The sequence number"},
{"pegin_bitcoin_tx", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The raw bitcoin transaction (in hex) depositing bitcoin to the mainchain_address generated by getpeginaddress"},
{"pegin_txout_proof", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A rawtxoutproof (in hex) generated by the mainchain daemon's `gettxoutproof` containing a proof of only bitcoin_tx"},
{"pegin_claim_script", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The witness program generated by getpeginaddress."},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's the script with version byte and witness program, not just witness program

},
},
},
Expand Down Expand Up @@ -2111,14 +2161,50 @@ UniValue createpsbt(const JSONRPCRequest& request)
);

std::vector<CPubKey> output_pubkeys;
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3], request.params[4], &output_pubkeys, false /* allow_peg_in */);
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], request.params[3], request.params[4], &output_pubkeys);

// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
psbtx.outputs[i].blinding_pubkey = output_pubkeys[i];
}

// Add peg-in stuff if it's there
for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
if (psbtx.tx->vin[i].m_is_pegin) {
CScriptWitness& pegin_witness = psbtx.tx->witness.vtxinwit[i].m_pegin_witness;
CAmount val;
VectorReader vr_val(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[0], 0);
vr_val >> val;
psbtx.inputs[i].value = val;
VectorReader vr_asset(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[1], 0);
vr_asset >> psbtx.inputs[i].asset;
VectorReader vr_genesis(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[2], 0);
vr_genesis >> psbtx.inputs[i].genesis_hash;
psbtx.inputs[i].claim_script.assign(pegin_witness.stack[3].begin(), pegin_witness.stack[3].end());

VectorReader vr_tx(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[4], 0);
VectorReader vr_proof(SER_NETWORK, PROTOCOL_VERSION, pegin_witness.stack[5], 0);
if (Params().GetConsensus().ParentChainHasPow()) {
Sidechain::Bitcoin::CTransactionRef tx_btc;
vr_tx >> tx_btc;
psbtx.inputs[i].peg_in_tx = tx_btc;
Sidechain::Bitcoin::CMerkleBlock tx_proof;
vr_proof >> tx_proof;
psbtx.inputs[i].txout_proof = tx_proof;
} else {
CTransactionRef tx_btc;
vr_tx >> tx_btc;
psbtx.inputs[i].peg_in_tx = tx_btc;
CMerkleBlock tx_proof;
vr_proof >> tx_proof;
psbtx.inputs[i].txout_proof = tx_proof;
}
pegin_witness.SetNull();
psbtx.tx->vin[i].m_is_pegin = false;
}
}

return EncodePSBT(psbtx);
}

Expand Down
22 changes: 22 additions & 0 deletions src/wallet/psbtwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <confidential_validation.h>
#include <pegins.h>
#include <wallet/psbtwallet.h>

TransactionError FillPSBTInputsData(const CWallet* pwallet, PartiallySignedTransaction& psbtx, bool bip32derivs)
Expand Down Expand Up @@ -95,6 +96,27 @@ TransactionError SignPSBT(const CWallet* pwallet, PartiallySignedTransaction& ps
}
}

// Stuff in the peg-in data
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
PSBTInput& input = psbtx.inputs[i];
if (input.value && input.peg_in_tx.which() != 0 && input.txout_proof.which() != 0 && !input.claim_script.empty() && !input.genesis_hash.IsNull()) {
CScriptWitness pegin_witness;
if (Params().GetConsensus().ParentChainHasPow()) {
const Sidechain::Bitcoin::CTransactionRef& btc_peg_in_tx = boost::get<Sidechain::Bitcoin::CTransactionRef>(input.peg_in_tx);
const Sidechain::Bitcoin::CMerkleBlock& btc_txout_proof = boost::get<Sidechain::Bitcoin::CMerkleBlock>(input.txout_proof);
pegin_witness = CreatePeginWitness(*input.value, input.asset, input.genesis_hash, input.claim_script, btc_peg_in_tx, btc_txout_proof);
} else {
const CTransactionRef& elem_peg_in_tx = boost::get<CTransactionRef>(input.peg_in_tx);
const CMerkleBlock& elem_txout_proof = boost::get<CMerkleBlock>(input.txout_proof);
pegin_witness = CreatePeginWitness(*input.value, input.asset, input.genesis_hash, input.claim_script, elem_peg_in_tx, elem_txout_proof);
}
tx.vin[i].m_is_pegin = true;
tx.witness.vtxinwit[i].m_pegin_witness = pegin_witness;
// Set the witness utxo
input.witness_utxo = GetPeginOutputFromWitness(tx.witness.vtxinwit[i].m_pegin_witness);
}
}

// This is a convenience/usability check -- it's not invalid to sign an unbalanced transaction, but it's easy to shoot yourself in the foot.
if (!imbalance_ok) {
// Get UTXOs for all inputs, to check that amounts balance before signing.
Expand Down
Loading