New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BIP 174 Partially Signed Bitcoin Transactions serialization and RPCs #12136

Closed
wants to merge 8 commits into
base: master
from

Conversation

@achow101
Member

achow101 commented Jan 10, 2018

BIP 174 specifies a binary transaction format which contains the information necessary for a signer to produce signatures for the transaction and holds the signatures for an input while the input does not have a complete set of signatures.


BIP 174 is fully implemented in this pull request. It contains a struct for the a PSBT, serialization and deserialization functions, and a PSBT specific versions of ProduceSignature (SignPartialTransaction), SignStep(SignSigsOnly) and CombineSignatures(FinalizePartialTransaction`).

Currently PSBT functionality is limited to 4 new RPC calls, walletupdatepsbt, walletcreatepsbt, combinepsbt, and decodepsbt.

walletupdatepsbt updates a given PSBT with data from the wallet. For each input, it will attempt to add the proper UTXO, sign for the input, and finalize the input. It will also add any known redeem scripts, witness scripts, and public key derivation paths if they are known to the wallet.

walletcreatepsbt takes a network serialized raw transaction as produced by the *rawtransaction commands and converts it to a PSBT. Inputs are filled using information known to the wallet.

combinepsbt combines multiple PSBTs and finalizes them if possible.

decodepsbt decodes a PSBT and into a human readable format in order to more easily examine them. It is analogous to decoderawtransaction.


All of the test vectors currently in BIP 174 are also implemented.

@@ -302,6 +381,218 @@ struct Stacks
};
}
// Iterates through all inputs of the partially signed transaction and just produces signatures for what it can and adds them to the psbt partial sigs
bool SignPartialTransaction(PartiallySignedTransaction& psbt, const CKeyStore* keystore, int nHashType)

This comment has been minimized.

@jonasschnelli

jonasschnelli Jan 10, 2018

Member

SignPartialTransaction sounds after we have partial transactions. Suggest SignPartialSignedTransaction.

This comment has been minimized.

@achow101

achow101 Apr 5, 2018

Member

Changed to SignPartiallySignedTransaction

@jonasschnelli

This comment has been minimized.

Member

jonasschnelli commented Jan 10, 2018

Great work!
General concept ack, though the PR is large and maybe there is a way to make smaller steps towards BIP174 (reduce of risks).

@nopara73 nopara73 referenced this pull request Jan 10, 2018

Open

Evaluate BIP147 #60

@achow101

This comment has been minimized.

Member

achow101 commented Jan 10, 2018

I'm not sure what's causing the travis failure.

@gmaxwell

This comment has been minimized.

Member

gmaxwell commented Jan 11, 2018

Concept ACK!

@dcousens

This comment has been minimized.

Contributor

dcousens commented Jan 16, 2018

Concept ACK

@fanquake fanquake added the Consensus label Jan 19, 2018

@laanwj

This comment has been minimized.

Member

laanwj commented Feb 14, 2018

Neat! Concept ACK.

switch (whichType)
{
case TX_PUBKEY:
script_ret.push_back(psbt.inputs[i].partial_sigs.find(CPubKey(vSolutions[0]))->second);

This comment has been minimized.

@instagibbs

instagibbs Feb 14, 2018

Member

this may segfault when there is no corresponding partial_sig, I believe this is the cause of your test failure.

This comment has been minimized.

@achow101
}
// Get public keys if hd is enabled
if (pwallet->IsHDEnabled()) {
if (type == TX_PUBKEYHASH) {

This comment has been minimized.

@instagibbs

instagibbs Feb 20, 2018

Member

should be:

if (type == TX_PUBKEYHASH || type == TX_WITNESS_V0_KEYHASH) {

This comment has been minimized.

@achow101
// Add to inputs
psbtx.inputs.push_back(input);
}
}

This comment has been minimized.

@instagibbs

instagibbs Feb 20, 2018

Member

this isn't mentioned in the spec anywhere, but passing along the the change output's pubkey(s)/hdpath(s)/redeemscript allows hww to understand change outputs.

This comment has been minimized.

@achow101

This comment has been minimized.

@achow101

achow101 Feb 21, 2018

Member

I decided to make this an option instead of the default. I'll add tests for it later.

This comment has been minimized.

@laanwj

laanwj May 31, 2018

Member

Should that be mentioned in the spec, then?

@achow101

This comment has been minimized.

Member

achow101 commented Feb 20, 2018

Rebased and squashed fixup commits.

@@ -4082,6 +4163,8 @@ UniValue walletcreatepsbt(const JSONRPCRequest& request)
"using fundrawtransaction.\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
"2. \"include_output_info\" (boolean, optional, default=false) If true, returns the PSBT with the redeem scripts, witness\n"

This comment has been minimized.

@instagibbs

instagibbs Feb 21, 2018

Member

you need to edit number of allowed args, and include the optional one in parens above.

This comment has been minimized.

@instagibbs

instagibbs Feb 21, 2018

Member

you also need to add it to the list of arguments that will be parsed as json rather than strings in rpc/client.cpp

This comment has been minimized.

@achow101

achow101 Feb 22, 2018

Member

Dang, I knew I forgot something. Fixed.

if (type == TX_PUBKEYHASH || type == TX_WITNESS_V0_KEYHASH) {
uint160 hash(solns[0]);
CKeyID keyID(hash);
if (!pwallet->HaveKey(keyID)) {

This comment has been minimized.

@instagibbs

instagibbs Feb 21, 2018

Member

What we really care about is that we have the hdkeypath, not particularly that the key is present or not. I don't think these HaveKey checks are necessary since add_keypath_to_map will deal with that case specifically.

This comment has been minimized.

@achow101

achow101 Feb 22, 2018

Member

Right, fixed.

@instagibbs

"ERROR: walletcreatepsbt argument 2 (named include_output_info in vRPCConvertParams) is not defined in dispatch table
ERROR: walletupdatepsbt argument 3 (named include_output_info in vRPCConvertParams) is not defined in dispatch table"

You need to add the new named args to the dispatch table

@@ -102,6 +102,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
{ "walletupdatepsbt", 2, "psbtformat"},
{ "walletupdatepsbt", 3, "include_output_info"},
{ "walletcreatepsbt", 2, "include_output_info"},

This comment has been minimized.

@instagibbs

instagibbs Feb 22, 2018

Member

should be 1

This comment has been minimized.

@achow101
@instagibbs

This comment has been minimized.

Member

instagibbs commented Mar 6, 2018

light tACK

I have rebased the externalhd branch onto this PR, with minor modifications, for ledger support, combined with @achow101 's HWI repo for signing. https://github.com/instagibbs/bitcoin/tree/external_psbt

@instagibbs

This comment has been minimized.

Member

instagibbs commented Apr 5, 2018

needs rebase

@achow101

This comment has been minimized.

Member

achow101 commented Apr 5, 2018

Rebased

1 similar comment
@achow101

This comment has been minimized.

Member

achow101 commented Apr 26, 2018

Rebased

@sipa

First set of comments on serialization code.

@@ -441,3 +441,60 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script)
}
return false;
}
PartiallySignedTransaction::PartiallySignedTransaction() : tx(), redeem_scripts(), witness_scripts(), inputs() {}
PartiallySignedTransaction::PartiallySignedTransaction(CMutableTransaction& tx, std::map<uint160, CScript>& redeem_scripts, std::map<uint256, CScript>& witness_scripts, std::vector<PartiallySignedInput>& inputs)

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

This constructor can take const reference arguments.

sanitize_for_serialization();
}
void PartiallySignedTransaction::sanitize_for_serialization()

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

Nit: method naming convention

if (psbt_in.non_witness_utxo) {
vout = psbt_in.non_witness_utxo->vout[in.prevout.n];
}
else if (!psbt_in.witness_utxo.IsNull()) {

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

Nit: else on the same line as } (and elsewhere).

// Remove sigs from inputs
for (unsigned int i = 0; i < tx.vin.size(); i++) {
CTxIn& in = tx.vin[i];
PartiallySignedInput psbt_in = inputs[i];

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

You can avoid making a copy here (use a const reference instead).

// Note: These constants are in reverse byte order because serialization uses LSB
static const uint32_t PSBT_MAGIC_BYTES = 0x74627370;

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

These can be marked constexpr instead.

if (use_in_index) {
WriteCompactSize(s, sizeof(PSBT_NUM_IN_VIN));
s << PSBT_NUM_IN_VIN;
WriteCompactSize(s, sizeof(psbt_in.index));

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

This line and the next write the index as a 4-byte uint32, not a compact size as described by BIP174.

The correct way to follow the BIP would be to first write the size of the compactsize-encoded input index in compactsize encoding, and then the compactsize encoding of the input. This is a lot easier if you'd have a single-argument SerializeToVector first (see above).

Also, this record requires 4 bytes (for smallish transactions), while a separator is only 1. It's only advantageous to write it everywhere (as opposed to having separators for already-signed inputs) if over 75% of the inputs are already signed. Perhaps it's generally better to use the separator approach instead?

This comment has been minimized.

@achow101

achow101 Apr 28, 2018

Member

This is a lot easier if you'd have a single-argument SerializeToVector first (see above).

Above where?

I'm not necessarily sure that that will always work due to the different data types.

Perhaps it's generally better to use the separator approach instead?

I think it generally is better to use the separator approach. By default, this code does not use input indexes unless a psbt that uses input indexes was provided (at least that's what it is supposed to do). That option was provided at the suggestion of someone else in order to handle very large transactions where almost all inputs were signed.

This comment has been minimized.

@sipa

sipa Apr 30, 2018

Member

Above where?

e25f290#r184790215

I'm not necessarily sure that that will always work due to the different data types.

It should. Char vectors are always serialized as compactsize-in-bytes + data-bytes. SerializeToVector first serializes to a vector, and then you serialize that. I can create a small example if you want.

This is still writing the input index as a 4-byte int, rather than as a compactsize, btw.

uint64_t value_len = ReadCompactSize(s);
// Do stuff based on type
switch(type)

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

Nit: { on the same line as switch.

PartiallySignedTransaction(CMutableTransaction& tx, std::map<uint160, CScript>& redeem_scripts, std::map<uint256, CScript>& witness_scripts, std::vector<PartiallySignedInput>& inputs);
template <typename Stream>
inline void Serialize(Stream& s) const {

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

Overall comment on the serialization code: you could simplify things a lot by also having a single-argument SerializeToVector (which takes an object but no header byte).

All instances of WriteCompactSize(s, ::GetSerializeSize(foo)); s << foo; could then turn into s << SerializeToVector(foo);. There are dozens of cases of this.

s >> tx;
} else {
// Read in the transaction
CMutableTransaction mtx;

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

You can use CTransactionRef prev_tx; s >> prev_tx; instead here (and input.non_witness_utxo = std::move(prev_tx); below).

template <typename Stream>
inline void Unserialize(Stream& s) {

This comment has been minimized.

@sipa

sipa Apr 27, 2018

Member

In commit "Implement PSBT structures and un/serliazation methods per BIP 174":

General comment on deserialization code: check that keys and values (where relevant) have the expected length. Things like uint160(key.begin() + 1, key.end()) will assertion fail if the length is wrong.

@achow101

This comment has been minimized.

Member

achow101 commented Apr 28, 2018

Addressed all of @sipa's comments.

// Write any hd keypaths
for (auto keypath_pair : hd_keypaths) {
s << SerializeToVector(PSBT_BIP32_KEYPATH_SIGHASH, Span<const unsigned char>(keypath_pair.first.begin(), keypath_pair.first.size()));
WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t));

This comment has been minimized.

@sipa

sipa Apr 30, 2018

Member

Ah yes, indeed.

// Write the number of inputs
if (num_ins > 0) {
s << PSBT_NUM_IN_VIN;

This comment has been minimized.

@sipa

sipa Apr 30, 2018

Member

@achow101 Ok, makes sense.

But you're still writing PSB_NUM_IN_VIN without a keylength prefix here.

// Write any partial signatures
for (auto sig_pair : psbt_in.partial_sigs) {
s << SerializeToVector(PSBT_WITNESSSCRIPT_PARTIAL_SIG, Span<const unsigned char>(sig_pair.first.begin(), sig_pair.first.size()));
WriteCompactSize(s, sig_pair.second.size());

This comment has been minimized.

@sipa

sipa Apr 30, 2018

Member

This and the line below can be written as s << sig_pair.second, I think - it's just an unsigned char vector.

if (use_in_index) {
WriteCompactSize(s, sizeof(PSBT_NUM_IN_VIN));
s << PSBT_NUM_IN_VIN;
WriteCompactSize(s, sizeof(psbt_in.index));

This comment has been minimized.

@sipa

sipa Apr 30, 2018

Member

Above where?

e25f290#r184790215

I'm not necessarily sure that that will always work due to the different data types.

It should. Char vectors are always serialized as compactsize-in-bytes + data-bytes. SerializeToVector first serializes to a vector, and then you serialize that. I can create a small example if you want.

This is still writing the input index as a 4-byte int, rather than as a compactsize, btw.

@sipa

This comment has been minimized.

Member

sipa commented Apr 30, 2018

@achow101 Here's a commit with a few suggested improvements to the serialization code: sipa@8733102

@achow101

This comment has been minimized.

Member

achow101 commented May 1, 2018

I've included @sipa's serialization improvements.

@instagibbs

This comment has been minimized.

Member

instagibbs commented May 14, 2018

needs rebase

@achow101

This comment has been minimized.

Member

achow101 commented May 17, 2018

Rebased

@achow101

This comment has been minimized.

Member

achow101 commented May 26, 2018

Rebased this and did some reorganization.

I squashed down a lot of the commits so that the changes to different parts aren't scattered around in different commits.

I also de-duplicated the signing code by using a PSBT specific SIgningProvider and SignatureCreator. The SigningProvider handles providing pubkeys and scripts from the PSBT itself along with the wallet and the SignatureCreator adds signatures and public keys directly to the PSBT once the signature is created. This avoids the previous code duplication.

Hopefully the reorganization and the PSBT specific SigningProvider and SignatureCreator will make this easier to review.

@@ -91,6 +91,15 @@ class base_blob
((uint64_t)ptr[7]) << 56;
}
uint32_t GetUint32(int pos) const
{
const uint8_t* ptr = data + pos * 8;

This comment has been minimized.

@laanwj

laanwj May 29, 2018

Member

why *8? that doesn't look in line with 32 bit values (also the uint64_t below should probably be uint32_t).

Also, please add LE to make clear that this is always little-endian.

This comment has been minimized.

@achow101

achow101 May 29, 2018

Member

Whoops. That's a bad copy-paste from the GetUint64 function above this one.

Fixed.

This comment has been minimized.

@kallewoof

kallewoof May 30, 2018

Member

Maybe I am seeing outdated code, but it still says * 8 here and uint64_t below.

@@ -145,6 +145,7 @@
'feature_blocksdir.py',
'feature_config_args.py',
'feature_help.py',
"rpc_psbt.py",

This comment has been minimized.

@laanwj

laanwj May 29, 2018

Member

Single quote like the others?

This comment has been minimized.

@achow101
@dcousens

This comment has been minimized.

Contributor

dcousens commented May 29, 2018

@achow101 have you thought about making this a 3rd party library for integration into other software, and then integration here?

@MarcoFalke

This comment has been minimized.

Member

MarcoFalke commented Jun 7, 2018

From travis:

The locale dependent function isdigit(...) appears to be used:
src/wallet/rpcwallet.cpp:            if (!std::isdigit(c)) {
Unnecessary locale dependence can cause bugs that are very
tricky to isolate and fix. Please avoid using locale dependent
functions if possible.
Advice not applicable in this specific case? Add an exception
by updating the ignore list in test/lint/lint-locale-dependence.sh
^---- failure generated from test/lint/lint-locale-dependence.sh
@achow101

This comment has been minimized.

Member

achow101 commented Jun 7, 2018

I think I fixed the linter error.

@achow101

This comment has been minimized.

Member

achow101 commented Jun 9, 2018

Apparently I lost some changes in a rebase or something. They should be back.

@achow101

This comment has been minimized.

Member

achow101 commented Jun 10, 2018

Rebased on top of #13425 which simplifies the signer logic.

CPubKey pubkey(vSolutions[0]);
keyID = pubkey.GetID();
if (!creator.CreateSig(provider, sig, keyID, scriptPubKey, sigversion)) return false;
sigdata.signatures.emplace(keyID, SigPair(pubkey, sig));

This comment has been minimized.

@sipa

sipa Jun 10, 2018

Member

Why is this needed? Signing a TX_PUBKEY should always result in a full signature.

This comment has been minimized.

@achow101

achow101 Jun 10, 2018

Member

A PSBT does not have to create the final scriptSig even when a complete set of signatures is available. The user has the option to not finalize the scriptSigs even when they can be.

This comment has been minimized.

@sipa

sipa Jun 10, 2018

Member

In that case you should also be able to deal with unfinalized signatures already present, and turn them into scriptSigs. In other words, you need to test whether sigdata.signatures contains a matching signature and reuse it. If that's the case, we're probably better off reintroducing SignatureDataSignatureCreator and SignatureDataSigningProvider, so these keys/script/signatures are automatically available everywhere.

Another interpretation is that TX_PUBKEY and TX_PUBKEYHASH etc just don't have "partial" forms - they're either fully signed or not, and as a result there never is anything to finalize.

This comment has been minimized.

@achow101

achow101 Jun 11, 2018

Member

We should discuss this on the mailing list as it requires changing the BIP.

For now, I have added a helper function for createSig which fetches and places signatures to and from the SignatureData.

CPubKey pubkey;
GetPubKey(&provider, &sigdata, keyID, pubkey);
sigdata.signatures.emplace(keyID, SigPair(pubkey, sig));

This comment has been minimized.

@sipa

sipa Jun 10, 2018

Member

Same here.

if (in_globals) {
num_ins = ReadCompactSize(s);
} else {
// Make sure that we are using inout indexes or this is the first input

This comment has been minimized.

@araspitzu

araspitzu Jun 11, 2018

Contributor

small typo: Make sure that we are using input indexes or this is the first input

This comment has been minimized.

@achow101
Inline Sign1 and SignN
Sign1 and SignN are kind of redundant so remove them and inline their
behavior into SignStep

@achow101 achow101 changed the title from Implement BIP 174 Partially Signed Bitcoin Transactions to Implement BIP 174 Partially Signed Bitcoin Transactions serialization and RPCs Jun 12, 2018

@achow101

This comment has been minimized.

Member

achow101 commented Jun 12, 2018

Updated the title to indicate that this is primarily serializations and RPCs.

achow101 added some commits Jun 8, 2018

Make SignatureData able to store signatures and scripts
In addition to having the scriptSig and scriptWitness, have SignatureData
also be able to store just the signatures (pubkeys mapped to sigs) and
scripts (script ids mapped to scripts).

Also have DataFromTransaction be able to extract signatures and scripts
from the scriptSig and scriptWitness of an input to put them in SignatureData.

Adds a new SignatureChecker which takes a SignatureData and puts pubkeys
and signatures into it when it successfully verifies a signature.

Adds a new field in SignatureData which stores whether the SignatureData
was complete. This allows us to also update the scriptSig and
scriptWitness to the final one when updating a SignatureData with another
one.
Replace CombineSignatures with ProduceSignature
Instead of using CombineSignatures to create the final scriptSig or
scriptWitness of an input, use ProduceSignature itself.

To allow for ProduceSignature to place signatures, pubkeys, and scripts
that it does not know about, we pass down the SignatureData to SignStep
which pulls out the information that it needs from the SignatureData.
Remove CombineSignatures and replace tests
Removes CombineSignatures and replaces its use in tests with
ProduceSignature to test the same behavior for ProduceSignature.

achow101 added some commits Sep 27, 2017

Create RPCs for PSBT
walletupdatepsbt takes a PSBT format transaction, updates the
PSBT with any inputs related to this wallet, and signs the transaction

walletcreatepsbt takes a raw network serialized transaction like
what createrawtransaction and fundrawtransaction output and converts
it to a PSBT using whatever information about the inputs it knows
from the wallet.

decodepsbt takes a PSBT and decodes it to JSON

combinepsbt takes multiple PSBTs for the same tx and combines them.
@luke-jr

This comment has been minimized.

Member

luke-jr commented Jun 16, 2018

If the only reason not to use protobufs is "another dependency", that's a bad reason to avoid it. Dependencies are better than reinventing the same thing... no reason to avoid them. (not to mention that protobuf is already a dependency)

@sipa

This comment has been minimized.

Member

sipa commented Jun 16, 2018

@luke-jr They also have a non-unique encoding, and are too complicated for what we need. Protobuf is also not a dependency already of all (or even most) software (including hardware devices) we expect to implement this, while all of those already need to implement Bitcoin P2P-like serialization, which this extends.

The discussion for that probably belongs on the ML.

@luke-jr

This comment has been minimized.

Member

luke-jr commented Jun 17, 2018

@sipa My goal was not to argue for a change in BIP 174, only to get a better explanation/rationale for why protobufs aren't used. :)

@araspitzu araspitzu referenced this pull request Jun 20, 2018

Closed

[WIP] Implement BIP174 #22

@achow101

This comment has been minimized.

Member

achow101 commented Jun 24, 2018

Since BIP 174 is changing soon, I'm closing this for now.

@achow101 achow101 closed this Jun 24, 2018

laanwj added a commit that referenced this pull request Jul 18, 2018

Merge #13557: BIP 174 PSBT Serializations and RPCs
020628e Tests for PSBT (Andrew Chow)
a4b06fb Create wallet RPCs for PSBT (Andrew Chow)
c27fe41 Create utility RPCs for PSBT (Andrew Chow)
8b5ef27 SignPSBTInput wrapper function (Andrew Chow)
58a8e28 Refactor transaction creation and transaction funding logic (Andrew Chow)
e9d86a4 Methods for interacting with PSBT structs (Andrew Chow)
12bcc64 Add pubkeys and whether input was witness to SignatureData (Andrew Chow)
41c607f Implement PSBT Structures and un/serialization methods per BIP 174 (Andrew Chow)

Pull request description:

  This Pull Request fully implements the [updated](bitcoin/bips#694) BIP 174 specification. It is based upon #13425 which implements the majority of the signing logic.

  BIP 174 specifies a binary transaction format which contains the information necessary for a signer to produce signatures for the transaction and holds the signatures for an input while the input does not have a complete set of signatures.

  This PR contains structs for PSBT, serialization, and deserialzation code. Some changes to `SignatureData` have been made to support detection of UTXO type and storing public keys.

  ***

  Many RPCs have been added to handle PSBTs.

  `walletprocesspsbt` takes a PSBT format transaction, updates the PSBT with any inputs related to this wallet, signs, and finalizes the transaction. There is also an option to not sign and just update.

  `walletcreatefundedpsbt` creates a PSBT from user provided data in the same form as createrawtransaction. It also funds the transaction and takes an options argument in the same form as `fundrawtransaction`. The resulting PSBT is blank with no input or output data filled in. It is analogous to a combination of `createrawtransaction` and `fundrawtransaction`

  `decodepsbt` takes a PSBT and decodes it to JSON. It is analogous to `decoderawtransaction`

  `combinepsbt` takes multiple PSBTs for the same tx and combines them. It is analogous to `combinerawtransaction`

  `finalizepsbt` takes a PSBT and finalizes the inputs. If all inputs are final, it extracts the network serialized transaction and returns that instead of a PSBT unless instructed otherwise.

  `createpsbt` is like `createrawtransaction` but for PSBTs instead of raw transactions.

  `convertpsbt` takes a network serialized transaction and converts it into a psbt. The resulting psbt will lose all signature data and an explicit flag must be set to allow transactions with signature data to be converted.

  ***

  This supersedes #12136

Tree-SHA512: 1ac7a79e5bc669933f0a6fcc93ded55263fdde9e8c144a30266b13ef9f62aacf43edd4cbca1ffbe003090b067e9643c9298c79be69d7c1b10231b32acafb6338
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment