Skip to content

Conversation

eranrund
Copy link
Contributor

@eranrund eranrund commented Apr 9, 2025

Right now, the output from the TransactionBuilder is an UnsignedTx, which contains all the material needed to produce a Tx that can be submitted to consensus, with the exception of anything that requires the private account keys. This allows separating the majority of transaction construction and transaction signing, and provides the groundwork for hardware wallet support. This separation makes the full-service hardware wallet support possible.

There's an important caveat here: the output TxOuts, together with their memos, are created and bundled in the UnsignedTx. For most memos that is fine, however this means that for authenticated sender memos, which require the spend private key, the caller has two options:

  1. Have access to the spend private view key when producing the UnsignedTx, so that they can sign their authenticated sender memo (for example, mobilecoind does this)
  2. Not create an RTH authenticated sender memo

This is not ideal since we want all of our clients, including full-service with it's hardware wallet support, to construct RTH authenticated sender memos.

The solution I am proposing in this PR is a necessary evil to enable this. In this PR, we move away from having the TransactionBuilder output an UnsignedTx, to having it output a new object I have named TxBlueprint.
The TxBlueprint and UnsignedTx contain mostly similar information, with the main difference being that a TxBlueprint contains information on how to build the output TxOuts, but does not contain the actual output TxOut themselves. The important implication of this is that the TxBlueprint can be constructed without a MemoBuilder, which means it does not need access to any sensitive key material (remember: prior to this PR, the RTHMemoBuilder requires a SenderMemoCredential instance, which requires the spend private key!).
A TxBlueprint, together with a MemoBuilder instance, can be converted into an UnsignedTx, and from there the flow is similar to how things work today (the UnsignedTx needs to be signed in order to produce a Tx that can be sent to consensus).

To reiterate, the flow today is:

  1. Create a TransactionBuilder, passing a MemoBuilder. If RTH Authenticated Sender memos are desirable, access to the private spend key is needed at this stage.
  2. Use the TransactionBuilder to create an UnsignedTx
  3. (Optionally move UnsignedTx to a different machine/service)
  4. The UnsignedTx can be signed with an AccountKey or a hardware wallet, producing a Tx

After this. change, the flow will be:

  1. Create a TransactionBuilder
  2. Use TransactionBuilder to produce a TxBlueprint
  3. (Optionally move TxBlueprint to a different machine/service)
  4. Convert TxBlueprint into an UnsignedTx by providing a MemoBuilder (which may require access to the sensitive key material)
  5. The UnsignedTx can be signed with an AccountKey or a hardware wallet, producing a Tx.

Helper methods make this change a little less intrusive than it might sound. However, there are some important side-effect worth mentioning:

  1. Right now, when outputs are added to a TransactionBuilder, memos are constructed and the MemoBuilder could return an error. For example, adding duplicate change outputs is not supported by all memo builders, and attempting to add more than one change output will result in an error when calling add_change_output. SInce memo building is now being postponed to a later stage, this will not be caught early. The user will still encounter the error, but only when they attempt to go from a TxBlueprint to an UnsignedTx. In my opinion this is an acceptable tradeoff since almost all cases (and essentially in all cases where we use TransactionBuilder ourselves), we do not expect to encounter these errors - these errors indicate misuse of the TransactionBuilder API.
  2. Right now, when outputs are added to a TransactionBuilder, the caller gets the final TxOut that will be included in the transaction. Since we're not producing that TxOut at this stage, this is no longer possible. Instead, the caller gets the TxOut public key, which makes it easy for them to later find the TxOut if they need to (after obtaining the UnsignedTx from the TxBlueprint). This hasn't proved to be problematic in this repo and in full-service, and I suspect will not be problematic in the mobile SDKs either.

I want to mention one other potential approach that was considered: One other approach here is to not modify TransactionBuilder as excessively as I've done here, and instead figure out some hack to replace the Authenticated Sender Memo of the output TxOut before signing a transaction. This will be a much smaller change, but I think it's an unreasonable approach - having some caveat that in certain cases a TxOut might change in an UnsignedTx just before signing seems like a major foot gun. As such, I went with the much more elaborate TxBlueprint approach.
I'm open to other suggestions, but the reality is that I don't think there's an elegant solution here because the memos right now get created very early on, and it's possible for a memo to require access to private key material, so that has to move "up the stack" to where signing takes place.

Copy link

github-actions bot commented Apr 9, 2025

⚠️ Downstream repo mobilecoinofficial/android-bindings failed to build. Check actions status for details.

Copy link

github-actions bot commented Apr 9, 2025

⚠️ Downstream repo mobilecoinofficial/full-service failed to build. Check actions status for details.

Copy link

⚠️ Downstream repo mobilecoinofficial/android-bindings failed to build. Check actions status for details.

Copy link

⚠️ Downstream repo mobilecoinofficial/android-bindings failed to build. Check actions status for details.

Copy link

⚠️ Downstream repo mobilecoinofficial/full-service failed to build. Check actions status for details.

1 similar comment
Copy link

⚠️ Downstream repo mobilecoinofficial/full-service failed to build. Check actions status for details.

…gnedTx`.

This allows us to postpone memo creation to a later point, laying the
groundwork for hw-wallet-signed RTH memos.
Copy link

⚠️ Downstream repo mobilecoinofficial/android-bindings failed to build. Check actions status for details.

Copy link

⚠️ Downstream repo mobilecoinofficial/full-service failed to build. Check actions status for details.

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.

1 participant