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

ADP-919: Sign Transactions Endpoint #2943

Merged
merged 20 commits into from
Oct 5, 2021
Merged

Conversation

KtorZ
Copy link
Member

@KtorZ KtorZ commented Oct 1, 2021

  • 📍 Remove withdrawal from the sign-transaction API.
    Withdrawals are 'needed' in this context when they refer to an external wallet (i.e. via a mnemonic phrase) so that, the required signature for such withdrawal can be produced (derived from the root key associated with the mnemonic). However:

    (a) The use-case for such external withdrawals stem from the ITN rewards, which were distributed more than 14 months ago to Shelley wallets that had participated in the incentivized testnet. It is reasonable to think that most concerned users have already redeemed their rewards. Those who haven't can still do it via the old transaction endpoint.

    (b) Having withdrawal as part of this API endpoint is awkward and confusing. Instead, it would be more logical / practical to perform the signature during the construction of the transaction; that is, when constructing a transaction with an external withdrawal, the result would already contain a signature from the associated mnemonic. Such transaction would then still need to be passed to the sign-transaction endpoint to gather signature from the wallet itself.

    Alternatively, not supporting this feature through the new transaction construction endpoint is also a very sensible choice. Regardless of the option, worrying about it now is unnecessary / a waste of time.

  • 📍 Extract vk signing from 'mkTx'
    Note that, doing so I have slightly altered the semantic of the function which can no longer return a 'ErrKeyNotFoundForAddress'.

    Not finding the relevant key for a given input will now simply skip that input. Ultimately, the code used to yield to a 500 server errors BUT, that case was truly unreachable. Proof is that there's no end-to-end test which happen to be looking for that error case since it cannot (must not) occur under any circumstances. It really is a deep server error.

    This change does not however makes the error 'silent', but merely defers it to the submission. If a necessary signature is missing, the transaction will eventually be rejected by the local node which will inform the user about the missing key.

    The rationale for changing the semantic is that, in the context of signing 'external' transactions, we want to allow other inputs than ours. This is crucial in the context of Alonzo since transactions may contain and consume script inputs. So it's important for this operation to skip such inputs and simply care about signing inputs relevant of the wallet.

    Note that, as a TODO, we should also now check for extra required signatures which can be specified in the transaction body.

  • 📍 Extend transaction layer with 'addVkWitnesses' and use it.

  • 📍 Move withdrawal signing into 'signTransaction'
    So that we avoid too many repetition in the upcoming implementation of addVkWitnesses, and also, centralize the creation of signatures into one place. Note that this does not change the semantic of the function and it works regardless of where the reward account comes from (external or internal).

  • 📍 Implement 'addVkWitnesses' for the shelley target.
    A bit of repetition here because of the encapsulated era which can't leave its scope. All-in-all, it's not a big deal and someone has to deal with the multiple eras into some place, might it be via a type-class or via similar branches in a case like here. Note also that in case of Byron transactions, this does not work, which is likely a fair limitation.

  • 📍 Add basic end-to-end test for signing
    The test does a full cycle: construction > signing > submission.

    However, some elements of the signing response are currently stubbed out. Obtaining them (the serialized body and witnesses) is doable but also look like extra / needless work :thinking_face: ? I'd argue that returning both the full serialized tx AND the serialized body AND the serialized witnesses is redundant. It seems like the interface currently is the serialized transaction (also with the construction endpoint) so it'd make more sense to only return this. To be discussed.

  • 📍 Fix unit test after changing semantic of transaction construction
    This no longer fails on unknown inputs, but skip the input.

  • 📍 Add test rejecting unsigned Tx

  • 📍 Add new test for signing withdrawals

  • 📍 Fix compilation error on withdrawal test.

  • 📍 Add counter-example serialized transaction.

  • 📍 Add unit test for failed CBOR decoding of tx w/ withdrawal
    This unit test comes from failed integration test for signing transactions.

  • 📍 Decode transaction bytes from base16 before deserialising them 🤦.

  • 📍 Rely on 'SealedTx' JSON instance for serialization (Base64)
    And do not provide raw bytes which are automatically serialized as Base16

  • 📍 Factor out submission code from all new-tx sign tests.

  • 📍 Remove redundant response elements from sign transaction result.
    The rationale is that, these elements are already present in the serialized transaction and, we assume that a client able to consume parts of the serialized transaction and put them together manually into a full serialized transaction would know anyway how to handle CBOR in the first place. Thus, splitting the full serialized transactions into smaller part if left as a client concern.

  • 📍 Add e2e test scenario illustrating requiredSigners for transactions
    This currently fails, as expected, since the feature isn't yet implemented in the API handler.

  • 📍 Include signatures for 'extra-key-witnesses' in transactions
    Note however that the wallet will only include signatures for keys it knows of, that is, for keys that are within the current visible window of the sequential state. That is okay because we do not expect the wallet to actually 'have' keys outside of the tracked window.

@KtorZ KtorZ self-assigned this Oct 1, 2021
@KtorZ KtorZ mentioned this pull request Oct 1, 2021
10 tasks
@KtorZ
Copy link
Member Author

KtorZ commented Oct 1, 2021

TODO:

  • More test scenarios to cover additional use-cases (see end of e2e spec file, some bullet points)
  • Support for extra required signatures which is a special field introduced in Alonzo that may contain extra signatures. Those keys need to be resolved somehow but it's unclear how (the wallet doesn't keep track of keys per se, but of addresses...)
  • Challenge / Discuss the current choice of response for the sign-transaction API. I believe that the current response is redundant and adds extra work for little benefits? (see commit notes)

cc @sevanspowell

(NB: the second point may be a known limitation we accept for now in order to get things unlocked a bit).

@KtorZ KtorZ force-pushed the KtorZ/ADP-919/sign-transaction branch 2 times, most recently from 356a34e to 5118cf4 Compare October 4, 2021 09:46
-- - Signing with pre-existing witnesses
-- - Signing with collaterals
-- - Signing with extra required signatures
-- - ideas?
Copy link
Contributor

Choose a reason for hiding this comment

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

There are actually several cases in this file that were created only up to "Construct" that, I guess, can be now filled with sign and submit steps. They all have relevant comment:

TODO: now we should sign it and send it in two steps

or

TODO: sign

]
r <- request @ApiTxId ctx submitEndpoint headers (NonJson $ BL.fromStrict sealedTx)
verify r
[ expectResponseCode HTTP.status500
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps it would be nice to "catch" 500 and have some 40x here?

Copy link
Member Author

Choose a reason for hiding this comment

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

We thought the same, though not really relevant to this PR but I think the submitEndpoint should not return 500 errors for invalid transactions, since this is precisely a client error.

Copy link
Contributor

Choose a reason for hiding this comment

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

I've issued bug ticket for this ADP-1172.

@ghost ghost mentioned this pull request Oct 4, 2021
(xprv, Passphrase pwd) <- keystore addr
let sigData = tx ^. #txId . #getHash
let sig = CC.unXSignature $ CC.sign pwd (getKey xprv) sigData
return $ xpubToBytes (getKey $ publicKey xprv) <> sig
Copy link
Member Author

Choose a reason for hiding this comment

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

Not exactly relevant to this PR, but necessary because of the change of semantic when signing / the removal of the ErrKeyNotFoundForAddress

let bytes = unsafeFromHex "84a70081825820410a9cd4af08b3abe25c2d3b87af4c23d0bb2fb7577b639d5cfbdfe13a4a696c0c0d80018182583901059f0c7b9899793d2c9afaeff4fd09bedd9df3b8cb1b9c301ab8e0f7fb3c13a29d3798f1b77b47f2ddb31c19326b87ed6f71fb9a27133ad51b000001001d19d714021a000220ec03198d0f05a1581de1fb3c13a29d3798f1b77b47f2ddb31c19326b87ed6f71fb9a27133ad51b000000e8d4a510000e80a0f5f6"
let sealedTx = sealedTxFromBytes bytes
sealedTx `shouldSatisfy` isRight

Copy link
Member Author

Choose a reason for hiding this comment

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

We stumbled upon this while testing, and got surprised by the result so we made it a golden test. In the end, the roundtrip test below is actually pretty weak as it doesn't test most constituents of transactions. In particular, withdrawals aren't tested at all by this property 😬

iohk-bors bot added a commit that referenced this pull request Oct 5, 2021
2946: Lenient SealedTx decoding r=paweljakubas a=abailly-iohk

> This is my very first PR in cardano-wallet so my apologies if I broke some rules of the land :)

This simple PR allows clients to provide either a base16 or base64 encoding when submitting a transaction for signing and other endpoints relevant for the new signing process. It is based off of @KtorZ's #2943 PR as this strand of work was triggered by us tripping ourselves in the foot trying to sign a base16 encoded transaction.

Co-authored-by: Arnaud Bailly <[email protected]>
Co-authored-by: Pawel Jakubas <[email protected]>
iohk-bors bot added a commit that referenced this pull request Oct 5, 2021
2946: Lenient SealedTx decoding r=paweljakubas a=abailly-iohk

> This is my very first PR in cardano-wallet so my apologies if I broke some rules of the land :)

This simple PR allows clients to provide either a base16 or base64 encoding when submitting a transaction for signing and other endpoints relevant for the new signing process. It is based off of @KtorZ's #2943 PR as this strand of work was triggered by us tripping ourselves in the foot trying to sign a base16 encoded transaction.

Co-authored-by: Arnaud Bailly <[email protected]>
Co-authored-by: Pawel Jakubas <[email protected]>
iohk-bors bot added a commit that referenced this pull request Oct 5, 2021
2946: Lenient SealedTx decoding r=paweljakubas a=abailly-iohk

> This is my very first PR in cardano-wallet so my apologies if I broke some rules of the land :)

This simple PR allows clients to provide either a base16 or base64 encoding when submitting a transaction for signing and other endpoints relevant for the new signing process. It is based off of @KtorZ's #2943 PR as this strand of work was triggered by us tripping ourselves in the foot trying to sign a base16 encoded transaction.

Co-authored-by: Arnaud Bailly <[email protected]>
Co-authored-by: Pawel Jakubas <[email protected]>
@piotr-iohk
Copy link
Contributor

bors r+

iohk-bors bot added a commit that referenced this pull request Oct 5, 2021
2943: ADP-919: Sign Transactions Endpoint r=piotr-iohk a=KtorZ

- 📍 **Remove withdrawal from the sign-transaction API.**
    Withdrawals are 'needed' in this context when they refer to an external wallet (i.e. via a mnemonic phrase) so that, the required signature for such withdrawal can be produced (derived from the root key associated with the mnemonic). However:

  (a) The use-case for such external withdrawals stem from the ITN rewards, which were distributed more than 14 months ago to Shelley wallets that had participated in the incentivized testnet. It is reasonable to think that most concerned users have already redeemed their rewards. Those who haven't can still do it via the old transaction endpoint.

  (b) Having withdrawal as part of this API endpoint is awkward and confusing. Instead, it would be more logical / practical to perform the signature during the construction of the transaction; that is, when constructing a transaction with an external withdrawal, the result would already contain a signature from the associated mnemonic.  Such transaction would then still need to be passed to the sign-transaction endpoint to gather signature from the wallet itself.

  Alternatively, not supporting this feature through the new transaction construction endpoint is also a very sensible choice. Regardless of the option, worrying about it _now_ is unnecessary / a waste of time.

- 📍 **Extract vk signing from 'mkTx'**
    Note that, doing so I have slightly altered the semantic of the function which can no longer return a 'ErrKeyNotFoundForAddress'.

  Not finding the relevant key for a given input will now simply skip that input. Ultimately, the code used to yield to a 500 server errors BUT, that case was truly unreachable. Proof is that there's no end-to-end test which happen to be looking for that error case since it cannot (must not) occur under any circumstances. It really is a deep server error.

  This change does not however makes the error 'silent', but merely defers it to the submission. If a necessary signature is missing, the transaction will eventually be rejected by the local node which will inform the user about the missing key.

  The rationale for changing the semantic is that, in the context of signing 'external' transactions, we want to allow _other_ inputs than ours. This is crucial in the context of Alonzo since transactions may contain and consume script inputs. So it's important for this operation to skip such inputs and simply care about signing inputs relevant of the wallet.

  Note that, as a TODO, we should also now check for extra required signatures which can be specified in the transaction body.

- 📍 **Extend transaction layer with 'addVkWitnesses' and use it.**
  
- 📍 **Move withdrawal signing into 'signTransaction'**
    So that we avoid too many repetition in the upcoming implementation of addVkWitnesses, and also, centralize the creation of signatures into one place. Note that this does not change the semantic of the function and it works regardless of where the reward account comes from (external or internal).

- 📍 **Implement 'addVkWitnesses' for the shelley target.**
    A bit of repetition here because of the encapsulated era which can't leave its scope. All-in-all, it's not a big deal and someone has to deal with the multiple eras into some place, might it be via a type-class or via similar branches in a case like here. Note also that in case of Byron transactions, this does not work, which is likely a fair limitation.

- 📍 **Add basic end-to-end test for signing**
    The test does a full cycle: construction > signing > submission.

    However, some elements of the signing response are currently stubbed out. Obtaining them (the serialized body and witnesses) is _doable_ but also look like extra / needless work :thinking_face: ? I'd argue that returning both the full serialized tx AND the serialized body AND the serialized witnesses is redundant. It seems like the interface currently is the serialized transaction (also with the construction endpoint) so it'd make more sense to only return this. To be discussed.

- 📍 **Fix unit test after changing semantic of transaction construction**
    This no longer fails on unknown inputs, but skip the input.

- 📍 **Add test rejecting unsigned Tx**
  
- 📍 **Add new test for signing withdrawals**
  
- 📍 **Fix compilation error on withdrawal test.**
  
- 📍 **Add counter-example serialized transaction.**
  
- 📍 **Add unit test for failed CBOR decoding of tx w/ withdrawal**
  This unit test comes from failed integration test for signing transactions.

- 📍 **Decode transaction bytes from base16 before deserialising them 🤦.**
  
- 📍 **Rely on 'SealedTx' JSON instance for serialization (Base64)**
    And do not provide raw bytes which are automatically serialized as Base16

- 📍 **Factor out submission code from all new-tx sign tests.**
  
- 📍 **Remove redundant response elements from sign transaction result.**
    The rationale is that, these elements are already present in the serialized transaction and, we assume that a client able to consume parts of the serialized transaction and put them together manually into a full serialized transaction would know anyway how to handle CBOR in the first place. Thus, splitting the full serialized transactions into smaller part if left as a client concern.

- 📍 **Add e2e test scenario illustrating requiredSigners for transactions**
    This currently fails, as expected, since the feature isn't yet implemented in the API handler.

- 📍 **Include signatures for 'extra-key-witnesses' in transactions**
    Note however that the wallet will only include signatures for keys it knows of, that is, for keys that are within the current visible window of the sequential state. That is okay because we do not expect the wallet to actually 'have' keys outside of the tracked window.


Co-authored-by: KtorZ <[email protected]>
Co-authored-by: Arnaud Bailly <[email protected]>
Co-authored-by: IOHK <[email protected]>
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Oct 5, 2021

Build failed:

iohk-bors bot added a commit that referenced this pull request Oct 5, 2021
2946: Lenient SealedTx decoding r=paweljakubas a=abailly-iohk

> This is my very first PR in cardano-wallet so my apologies if I broke some rules of the land :)

This simple PR allows clients to provide either a base16 or base64 encoding when submitting a transaction for signing and other endpoints relevant for the new signing process. It is based off of @KtorZ's #2943 PR as this strand of work was triggered by us tripping ourselves in the foot trying to sign a base16 encoded transaction.

Co-authored-by: Arnaud Bailly <[email protected]>
Co-authored-by: Pawel Jakubas <[email protected]>
@KtorZ KtorZ force-pushed the KtorZ/ADP-919/sign-transaction branch from 9becd53 to c1606fb Compare October 5, 2021 12:58
@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

bors r+

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Oct 5, 2021

Canceled.

@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

bors r+

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Oct 5, 2021

Merge conflict.

@KtorZ KtorZ force-pushed the KtorZ/ADP-919/sign-transaction branch from b2b3c22 to 33eafc5 Compare October 5, 2021 13:50
@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

bors r+

@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

bors r-

KtorZ and others added 17 commits October 5, 2021 18:12
  Withdrawals are 'needed' in this context when they refer to an
  external wallet (i.e. via a mnemonic phrase) so that, the required
  signature for such withdrawal can be produced (derived from the root
  key associated with the mnemonic). However:

  (a) The use-case for such external withdrawals stem from the ITN
  rewards, which were distributed more than 14 months ago to Shelley
  wallets that had participated in the incentivized testnet. It is
  reasonable to think that most concerned users have already redeemed
  their rewards. Those who haven't can still do it via the old
  transaction endpoint.

  (b) Having withdrawal as part of this API endpoint is awkward and
  confusing. Instead, it would be more logical / practical to perform
  the signature during the construction of the transaction; that is,
  when constructing a transaction with an external withdrawal, the
  result would already contain a signature from the associated mnemonic.
  Such transaction would then still need to be passed to the
  sign-transaction endpoint to gather signature from the wallet itself.

  Alternatively, not supporting this feature through the new transaction
  construction endpoint is also a very sensible choice. Regardless of
  the option, worrying about it _now_ is unnecessary / a waste of time.
  Note that, doing so I have slightly altered the semantic of the
  function which can no longer return a 'ErrKeyNotFoundForAddress'.

  Not finding the relevant key for a given input will now simply skip
  that input. Ultimately, the code used to yield to a 500 server errors
  BUT, that case was truly unreachable. Proof is that there's no
  end-to-end test which happen to be looking for that error case since
  it cannot (must not) occur under any circumstances. It really is a
  deep server error.

  This change does not however makes the error 'silent', but merely
  defers it to the submission. If a necessary signature is missing, the
  transaction will eventually be rejected by the local node which will
  inform the user about the missing key.

  The rationale for changing the semantic is that, in the context of
  signing 'external' transactions, we want to allow _other_ inputs than
  ours. This is crucial in the context of Alonzo since transactions may
  contain and consume script inputs. So it's important for this
  operation to skip such inputs and simply care about signing inputs
  relevant of the wallet.

  Note that, as a TODO, we should also now check for extra required
  signatures which can be specified in the transaction body.
  So that we avoid too many repetition in the upcoming implementation of addVkWitnesses, and also, centralize the creation of signatures into one place. Note that this does not change the semantic of the function and it works regardless of where the reward account comes from (external or internal).
  A bit of repetition here because of the encapsulated era which can't leave its scope. All-in-all, it's not a big deal and someone has to deal with the multiple eras into some place, might it be via a type-class or via similar branches in a case like here. Note also that in case of Byron transactions, this does not work, which is likely a fair limitation.
  The test does a full cycle: construction > signing > submission.

  However, some elements of the signing response are currently stubbed
  out. Obtaining them (the serialized body and witnesses) is _doable_
  but also look like extra / needless work :thinking_face: ? I'd argue
  that returning both the full serialized tx AND the serialized body AND
  the serialized witnesses is redundant. It seems like the interface
  currently is the serialized transaction (also with the construction
  endpoint) so it'd make more sense to only return this. To be
  discussed.
  This no longer fails on unknown inputs, but skip the input.
This unit test comes from failed integration test for signing transactions.
  And do not provide raw bytes which are automatically serialized as Base16
  The rationale is that, these elements are already present in the serialized transaction and, we assume that a client able to consume parts of the serialized transaction and put them together manually into a full serialized transaction would know anyway how to handle CBOR in the first place. Thus, splitting the full serialized transactions into smaller part if left as a client concern.
  This currently fails, as expected, since the feature isn't yet implemented in the API handler.
@KtorZ KtorZ force-pushed the KtorZ/ADP-919/sign-transaction branch from ea30fc0 to 60b8075 Compare October 5, 2021 16:14
KtorZ and others added 3 commits October 5, 2021 18:53
  Note however that the wallet will only include signatures for keys it knows of, that is, for keys that are within the current visible window of the sequential state. That is okay because we do not expect the wallet to actually 'have' keys outside of the tracked window.
@KtorZ KtorZ force-pushed the KtorZ/ADP-919/sign-transaction branch from 60b8075 to fc0114c Compare October 5, 2021 16:53
@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

bors +

pretty please 🙏

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Oct 5, 2021

Did you mean "r+"?

@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

bors r+

@KtorZ
Copy link
Member Author

KtorZ commented Oct 5, 2021

I didn't miss you.

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Oct 5, 2021

Build succeeded:

@iohk-bors iohk-bors bot merged commit 807980f into master Oct 5, 2021
@iohk-bors iohk-bors bot deleted the KtorZ/ADP-919/sign-transaction branch October 5, 2021 17:40
WilliamKingNoel-Bot pushed a commit that referenced this pull request Oct 5, 2021
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.

4 participants