Skip to content

Conversation

@gwillen
Copy link
Contributor

@gwillen gwillen commented May 1, 2019

This PR extends the PSBT format and RPCs to handle Confidential Assets transactions.

New fields in PSBT inputs: Unblinded value, Value blinder, Unblinded asset, Asset blinder.
New fields in PSBT outputs: Recipient blinding pubkey, Value commitment, Value blinder, Asset commitment, Asset blinder, Nonce commitment, Range proof, Surjection proof.

We preserve the existing invariant that the unsigned transaction inside a PSBT never changes during the process; all updates that need to be applied to produce the final signed transaction are accumulated in the PSBT fields listed above.

The process is as follows:

  • Create a PSBT using converttopsbt [deprecated], walletcreatefundedpsbt, createpsbt
    • At this point, the output pubkey fields will be filled, for any outputs to confidential addresses.
    • Input fields may be filled, if the wallet was used; otherwise they will be filled in the next step.
    • Incremental creation of the unsigned transaction itself is OUTSIDE the scope of this work. The unsigned transaction must be fully populated with inputs and outputs before the PSBT is created.
    • Transactions with peg-in, issuance, or reissuance outputs are NOT supported at this time.
  • If necessary, update the psbt with input data using walletfillpsbtdata.
    • This is like the old walletprocesspsbt RPC [deprecated], which tried to both fill and sign the PSBT (which is not workable in the Confidential Assets setting.)
  • Once all inputs have had necessary data updated, possibly using multiple wallets if necessary, any wallet can be used to blind the transaction using blindpsbt.
    • This uses the input blinding data, along with the output blinding pubkeys, to compute the output blinding data.
    • Incremental blinding is not supported. All input blinding data must be available when blindpsbt is called.
  • Then, the blinded PSBT must be signed using walletsignpsbt. As with updating, this can be done by multiple wallets as necessary for the inputs being signed.
  • Once all signatures are present, finalizepsbt is used to create the final transaction in the regular transaction format, as before.
  • Then sendrawtransaction is used, which will check to make sure that blinding was performed properly before sending.

@gwillen gwillen requested a review from instagibbs May 1, 2019 09:38
@instagibbs
Copy link
Contributor

could you rebase?

Copy link
Contributor

@instagibbs instagibbs left a comment

Choose a reason for hiding this comment

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

concept ACK, first look seems good.

}

CAmount CWalletTx::GetOutputValueOut(unsigned int output_index) const {
void CWalletTx::GetNonIssuanceBlindingData(const unsigned int output_index, CPubKey* blinding_pubkey_out, CAmount* value_out, uint256* value_factor_out, CAsset* asset_out, uint256* asset_factor_out) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

this refactoring could be its own commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// One change script per output asset.
size_t index = 0;
for (const std::pair<CAsset, CAmount>& value : mapValue) {
for (const std::pair<const CAsset, CAmount>& value : mapValue) {
Copy link
Contributor

Choose a reason for hiding this comment

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

compiler complaint squashing could be its own commit

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this must have gone away in the rebase

Copy link
Contributor

Choose a reason for hiding this comment

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

ah yeah


CMutableTransaction tx_tmp = tx; // We don't want to mutate the transaction in the PSBT yet, just extract blinding data

// TODO(gwillen): Make this do something better than fail silently if there are any issuances, reissuances, pegins, etc.
Copy link
Contributor

Choose a reason for hiding this comment

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

PSBTs can't really encode these anyways yet, I think we're safe for now.

Supporting the creation of issuance/peg-in inputs in a PSBT format is the harder part.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, the PSBT can encode anything the CTransaction can encode, so I can make a PSBT of an issuance -- I just can't blind it. (Perhaps I also can't sign it -- I didn't try a fully unblinded one.)

o.range_proof = tx_tmp.witness.vtxoutwit[i].vchRangeproof;
o.surjection_proof = tx_tmp.witness.vtxoutwit[i].vchSurjectionproof;

o.blinding_pubkey = CPubKey(); // Once we're done blinding, remove the pubkeys to signal that it's complete
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this makes sense. We remove partial sigs once a full sig is complete right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, and I suspect that there's more pruning I could do with the goal of keeping the PSBTs small (which I expect is why we do that), but this specific removal is actually important as a signal to signpsbt that it's okay to sign. (Since the hack with the nonce field is gone in this flow.)

Copy link
Contributor

Choose a reason for hiding this comment

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

why wouldn't the presence of the other fields show that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only way to tell that an output is intended to be blinded is that it has a pubkey. Absence of blinders could mean "not going to be blinded" or "hasn't been blinded yet".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I guess you could indeed check for "has a pubkey and blinders" or "has no pubkey", but just checking for not having a pubkey seems easier (and is similar to what the code was doing before). But I can go either way as you like.


CMutableTransaction& tx = *psbtx.tx;

// TODO(gwillen): Replace all this with the 'bonus output' scheme to use an OP_RETURN to balance blinders, with a rangeproof exponent of -1 (public).
Copy link
Contributor

Choose a reason for hiding this comment

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

with known blinding pubkey, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe the scheme we talked about -- which I don't expect to fully understand before implementing it -- was to just make up a random pubkey and then throw it away after, because nobody ever needs to be able to rewind these outputs.

Copy link
Contributor

Choose a reason for hiding this comment

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

that works too. Could just be the generator point G or something stupid as well.

@gwillen gwillen force-pushed the feature-psbt-ca branch from ef7e009 to b6bb19a Compare May 2, 2019 01:14
@gwillen
Copy link
Contributor Author

gwillen commented May 2, 2019

Rebased, still need to split things up a bit. Also looks like I killed CI with the rebase, will investigate.

EDIT: nevermind, of course CI fails -- the tests don't pass with all the extra printfs in there. That will go away on its own when I remove those.

@gwillen gwillen force-pushed the feature-psbt-ca branch 2 times, most recently from d7a44e5 to 2921cb4 Compare May 3, 2019 00:21
@gwillen
Copy link
Contributor Author

gwillen commented May 3, 2019

apparently somehow I lost the compiler warning squashing in the rebase, but not because it was fixed upstream? So I added it back as its own commit.

Also fixed tests by removing all the stray printfs, none of them appeared worthy of upgrading to LogPrintf.

I think this should be ready for a real review.

@gwillen gwillen force-pushed the feature-psbt-ca branch from 51ad0ab to 33fe9b4 Compare May 3, 2019 01:12
@gwillen gwillen changed the title WIP: First pass at PSBT for Confidential Assets PSBT for Confidential Assets May 3, 2019
@gwillen gwillen force-pushed the feature-psbt-ca branch from 33fe9b4 to e4b9d43 Compare May 3, 2019 01:41
@gwillen
Copy link
Contributor Author

gwillen commented May 3, 2019

fixed travis complaints (coming from lints that don't run on my system because they have some python dependency I can't get to install.)

@instagibbs
Copy link
Contributor

instagibbs commented May 3, 2019 via email

@instagibbs
Copy link
Contributor

instagibbs commented May 6, 2019

ok looks like everything but bitcoin compatibility test for psbt is working:

test_framework.authproxy.JSONRPCException: TX decode failed Duplicate Key, output asset_commitment already provided: iostream error (-22)

see: test/bitcoin_functional/functional/rpc_psbt.py

@gwillen
Copy link
Contributor Author

gwillen commented May 6, 2019

Hm, interesting. It only fails on a single platform, which makes me worry it's nondeterministic. Also, I didn't realize those compatibility tests existed, so I wasn't running them -- I will try and see what it does locally.

ALSO, I think I'm still waiting for you to help me figure out where to condition on g_con_elementsmode. So assuming the compatibility tests run without that, that may fix it?

@gwillen
Copy link
Contributor Author

gwillen commented May 20, 2019

@instagibbs formal reminder that I am waiting for your review on this (and guidance on where to put g_con_elementsmode checks)

@instagibbs
Copy link
Contributor

@stevenroose promised a review Soon(TM), I think after his next catchup PR.

@gwillen
Copy link
Contributor Author

gwillen commented May 29, 2019

@stevenroose @instagibbs piiiing? :-) I would really like to get this cleaned up and in. I definitely still need help sorting out what g_con_elementsmode is for, and where it should fit in (to make the bitcoin backwards-compat tests pass, which still fail at present.)

@instagibbs
Copy link
Contributor

instagibbs commented May 29, 2019

please add tests for walletcreatefundedpsbt, I don't think it works correctly. It's adding the old hack-style nonce commitments as pubkeys and not filling out the blinding pubkeys as psbt fields which makes blinding impossible.

@gwillen
Copy link
Contributor Author

gwillen commented Jun 25, 2019

Ok so, belated comment on your last comment @instagibbs -- there already were tests for walletcreatefundedpsbt, but nothing is actually testing that things that should be blinded are, and nothing is testing that nonces don't show up where they shouldn't.

I have fixed walletcreatefundedpsbt and converttopsbt, and tested by hand, and the tests pass; but I haven't gone back and made the tests check that expected blinding is actually happening.

I expect that the code itself should be done at this point, though -- can you take another look?

"blindpsbt \"psbt\" ( ignoreblindfail )\n"
"\nUse the blinding data from the PSBT inputs to generate the blinding data for the PSBT outputs.\n"
"\nUse the blinding data from the PSBT inputs to generate the blinding data for the PSBT outputs.\n\n"
"TODO: Not expected to work on issuance/reissuance/peg transactions yet.\n"
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather not have a TODO in help text :) Just telling the user it isn't supported is enough

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah fair. Fixing.

// At this point, if the nonce field is present it should be a smuggled
// pubkey, and not a real nonce. Convert it back to a pubkey and strip
// it out.
psbtx.outputs[i].blinding_pubkey = CPubKey(tx.vout[i].nNonce.vchCommitment);
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to sanity check it's a IsFullyValid key?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaving this per discussion.

// If we're signing, check that the transaction is not still in need of blinding
if (sign) {
for (const PSBTOutput& o : psbtx.outputs) {
if (o.blinding_pubkey.IsValid()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I usually prefer IsFullyValid()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Leaving this per discussion.

import json
import os

import IPython
Copy link
Contributor

Choose a reason for hiding this comment

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

que?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

whoop lol

was using this for debugging. Removed.

@gwillen
Copy link
Contributor Author

gwillen commented Jul 2, 2019

Addressed comments, still have to debug the issue I uncovered with the walletcreatefundedpsbt change output coming out improperly unblinded.

@gwillen
Copy link
Contributor Author

gwillen commented Jul 3, 2019

Sigh, ok, gave up on doing createfundedpsbt in (what I felt was) the nice way. The mystery of FundTransaction runs way too deep. Instead, I let FundTransaction do its thing, then extract the pubkeys from the nonces afterwards.

Still need to rebase on 0.18.

@instagibbs
Copy link
Contributor

@gwillen right, seems to be the least painful thing to do. We let Core-ish stuff alone if it makes rebasing upstream easier.

@gwillen
Copy link
Contributor Author

gwillen commented Jul 3, 2019

@instagibbs Heh... I didn't even think about the ease-of-rebase issue. ^^;

I am still working on the rebase onto 0.18. Currently the rpc_psbt.py test is erroring out immediately at the top of the very first test with "insufficient funds", which confuses me and I'm not sure what's up.

@gwillen gwillen force-pushed the feature-psbt-ca branch 2 times, most recently from 5becc1e to c4f3605 Compare July 26, 2019 17:09
@gwillen
Copy link
Contributor Author

gwillen commented Jul 27, 2019

Rebased onto master.

After a bunch of fixups, the errors left from Travis are (1) an apparent memory-exhaustion running tsan, and (2) a timeout. Not sure what's the best way to deal with those but they're not obviously something I can fix.

@instagibbs
Copy link
Contributor

instagibbs commented Jul 27, 2019 via email

@instagibbs
Copy link
Contributor

Please update the OP to list some of the common usage sequences for RPC.

Also an explicit list of the new fields in the description is probably apt.

@instagibbs
Copy link
Contributor

oh could you also say what the PR isn't doing in the OP text? e.g., doesn't handle issuance/peg-ins.

@gwillen
Copy link
Contributor Author

gwillen commented Jul 29, 2019

Updated the PR description.

@gwillen
Copy link
Contributor Author

gwillen commented Jul 31, 2019

Addressed all comments. PTAL?

@instagibbs
Copy link
Contributor

Changes look good, one more linter:

./test/functional/rpc_psbt.py:493:58: E703 statement ends with a semicolon
266^---- failure generated from test/lint/lint-python.sh

@gwillen
Copy link
Contributor Author

gwillen commented Jul 31, 2019

Lol, it's really hard for me to not type semicolons at the end of lines. I blame C/C++ for this. Fixed. (One day I will get the python lints working on OS X.)

@gwillen
Copy link
Contributor Author

gwillen commented Jul 31, 2019

Added a sentence of explanation of output_pubkeys_out in ConstructTransaction. I did leave it in because there's still once place I did use it.

@gwillen
Copy link
Contributor Author

gwillen commented Jul 31, 2019

Squashed down to a few sensible commits.

@instagibbs instagibbs merged commit 3127c65 into ElementsProject:master Jul 31, 2019
@instagibbs
Copy link
Contributor

ACK

@gwillen
Copy link
Contributor Author

gwillen commented Jul 31, 2019

Thanks @instagibbs! Been a long road for this one.

@instagibbs
Copy link
Contributor

appreciate the heavy lifting!


// How many are we trying to blind?
int num_pubkeys = 0;
unsigned int keyIndex = -1;
Copy link
Member

Choose a reason for hiding this comment

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

Flagged by ubsan for being an implicit signedness conversion (which is not UB but ubsan lints for it). But also this is literally UB because keyIndex is never checked against this -1 value, it's just used as an array index.

Copy link
Member

Choose a reason for hiding this comment

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

Should be fixed by #900, just noting here for posterity.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, this block appears to have been copied from rawblindrawtransaction which has had this issue since at least #515

Copy link
Member

Choose a reason for hiding this comment

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

Oh, my bad, in both cases it is actually safe becasue keyIndex is only -1 if num_pubkeys is left at 0, and keyIndex is only used as an index when num_pubkeys is nonzero.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants