Skip to content
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

Wallet failing to spend after accepting a transfer that was collaboratively built by two wallets #253

Closed
St333p opened this issue Jul 30, 2024 · 8 comments · Fixed by #254
Assignees
Labels
bug Something isn't working
Milestone

Comments

@St333p
Copy link

St333p commented Jul 30, 2024

I'm trying to build a collaborative RGB transfer in which more than one wallet contribute inputs. The receiving wallet happily accepts the transfer but then it's not able to spend from the resulting allocation(s) with error: Completion(Stock("the provided state transition is not a part of the bundle"))

Here you can find the test that reproduces this issue (run cargo test --test transfers collaborative_transfer): https://github.com/St333p/rgb-integration-tests/tree/rgb_coinjoin

Note: this should be tested on rust 1.79 due to rust-amplify/amplify-derive#44

@St333p St333p changed the title Wallet failing to spend after accepting a transfer that was collaboratively built by two nodes Wallet failing to spend after accepting a transfer that was collaboratively built by two wallets Jul 30, 2024
@dr-orlovsky dr-orlovsky self-assigned this Jul 31, 2024
@dr-orlovsky dr-orlovsky added the bug Something isn't working label Jul 31, 2024
@dr-orlovsky dr-orlovsky added this to the v0.11.0 milestone Jul 31, 2024
@dr-orlovsky
Copy link
Member

Am I right that you are talking about payjoin or atomic swap transaction, and not some form of multisig?

@St333p
Copy link
Author

St333p commented Aug 5, 2024

Writing that test I was trying to distil a "minimal" scenario in which more than one wallet contributes inputs into a collaborative transaction as a simplification of coinjoin- or payjoin-like protocols; no multisig inputs are involved. In particular:

  • initial setup ends here: wlt_1 owns 400 tokens on utxo_1 and wlt_2 owns 200 on utxo_2.
  • here and in the following line, both wallets add their inputs to an empty PSBT in order to send 600 tokens to wlt_3
  • here and in the following line, both senders add their respective rgb data to the PSBT and wlt_2 also finalizes the PSBT
  • both sending wallets sign and the tx is broadcasted
  • senders consume the fascia and create their respective consignments
  • here wlt_3 accepts all the consignments created by senders but then it's not able to do a simple send of those funds

I don't know where the problem is, but the receiving wallet should either (1) be able to spend from that allocation or (2) avoid accepting the transfer.

@dr-orlovsky dr-orlovsky added question Further information is requested and removed bug Something isn't working labels Aug 5, 2024
@dr-orlovsky
Copy link
Member

This happens because you have two parties, and create Fascia just for the second one. So the method color_psbt_begin (whatever it means) must also return Fascia, which has to be consumed by the first actor.

Next, there must be two consignments created, from the first and second actor.

It can't be controlled at the level of RGB libraries since this is a multiparty protocol and RGB libraries (like Bitcoin libraries) can't orchestrate interactivity of multiparty actions - it is a requirement to the atomic swap protocol creators to define and enforce a specific workflow (like in PayJoin, CoinJoin, Lightning, MuSig and other multiparty protocols).

Re-iterating on how to do this properly. If you have multiple parties using the same witness transaction with their RGB contracts, each of them must do a full normal cycle of RGB operation construction, fascia construction etc independently. They also have to produce two different consignments - one each; and everybody who has two accept operations from both of them, must accept both of the consignments.

@St333p
Copy link
Author

St333p commented Aug 6, 2024

Thanks a lot for the information. My code is already building two consignments as you suggest as each sending wallet is performing its own call to create_consignments. It only involves one fascia because psbt.rgb_commit includes the commitment into the psbt so AFAIU it should only be called once since there should only be one single MPC commitment in the tx. However, this can be discussed elsewhere since the bug I'm trying to show here is on the receiving side.

Someone accepting a transfer shouldn't need to care about how the transfer was constructed. Once the transfer is successfully accepted and the allocation is recognized as owned - as can be seen through the additional log I added in the last commit (015b58f) - it should be spendable, lest the funds being effectively burnt. Note that wlt_3 is not really using custom methods, it just syncs, accepts the transfer and then tries to spend it using a thin wrapper around the native pay method.

@dr-orlovsky
Copy link
Member

It only involves one fascia because psbt.rgb_commit includes the commitment into the psbt so AFAIU it should only be called once since there should only be one single MPC commitment in the tx.

That is correct. And it has to be called after PSBT has been "colored" by all participants.

The original design which was planned was to return PSBT back to the first participant for signing, and that participant would be able to extract the same Fascia for himself (to consume into the stash) using Psbt::rgb_extract method. But this method is not yet implemented, since I left the multi-protocol workflows for v0.12:

https://github.com/RGB-WG/rgb/blob/b2bc6a2a9f824f908bcb554c710784e6b5397692/psbt/src/lib.rs#L128-L130

Someone accepting a transfer shouldn't need to care about how the transfer was constructed. Once the transfer is successfully accepted and the allocation is recognized as owned - as can be seen through the additional log I added in the last commit (015b58f) - it should be spendable

Sounds reasonable. Investigating the case once again to see what can be done here before we have proper multi-party APIs in v0.12

@dr-orlovsky
Copy link
Member

The source of the problem is the fact that both previous owners, creating multiparty PSBT, send funds to the same UTXO inside the witness transaction, which is owned by the new user. However, the user receives only a single consignment from one of the senders; thus it knowns the history of some coins, while the second state transition bundle allocating more coins to the same output is unknown to him.

I do not see how to solve this situation. If user will be able to spend from this UTXO, he will just loose part of the funds. We also can't allow the user not to accept the consignment, since there is nothing in the consignment which is invalid. The only solution I see here is the proper construction of multi-party protocols as a layer on top of RGB.

@dr-orlovsky dr-orlovsky transferred this issue from RGB-WG/rgb-core Aug 10, 2024
@dr-orlovsky dr-orlovsky added bug Something isn't working and removed question Further information is requested labels Aug 10, 2024
@dr-orlovsky
Copy link
Member

dr-orlovsky commented Aug 10, 2024

Further investigation has shown that this specific case you have reported was caused by a bug. Here is a simple solution, I have already checked that it makes your test to pass: #254

running 1 test
test collaborative_transfer has been running for over 60 seconds
test collaborative_transfer ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 75 filtered out; finished in 155.16s

However, this was possible due to the fact that the first participant hasn't concealed his state transition and the second one was able to extract it as a part of his Fascia and add it to the single consignment sent to the receiver.

What this mean that the actual problem related to the multi-peer PSBT-based protocols is theoretically still there, it is just that the test you have doesn't reveal it yet. This problem can't be solved at RGB level and requires a protocols on top (for instance, RGB with Lightning BOLT solves this problem for the case of BOLT channels, since both parties can deterministically generate state transitions of each other - so for the case of payjoins one needs to design a different protocol, like the one with two consignments etc).

@St333p
Copy link
Author

St333p commented Aug 12, 2024

Your PR fixes the problem, thanks. I also tried changing the test so that the participants conceal their state transitions and the accept_transfer indeed fails, but I see no problems there since the receiving wallet can't be tricked into accepting funds it won't be able to spend.

@github-project-automation github-project-automation bot moved this from In progress to Done in RGB release v0.11 Aug 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants