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

Multi-asset API extensions #2447

Merged
merged 12 commits into from
Jan 25, 2021
Merged

Multi-asset API extensions #2447

merged 12 commits into from
Jan 25, 2021

Conversation

rvl
Copy link
Contributor

@rvl rvl commented Jan 14, 2021

Issue Number

ADP-604

Overview

Starting implementation of API spec in #2446.

  • listAssets endpoint
  • getAsset endpoint
  • Added ApiWalletAssetsBalance which provides the balance of non-ade assets.
  • Removed WalletBalance from Primitive.Types and replaced ApiT WalletBalance with ApiWalletBalance.
  • Some kind of integration test for these endpoints.
  • Remove Quantity "lovelace" a where possible, except for the API.
  • TokenName inner type is now ByteString, add length validation.

Comments

@rvl rvl added the ADDING FEATURE Mark a PR as adding a new feature, for auto-generated CHANGELOG label Jan 14, 2021
@rvl rvl self-assigned this Jan 14, 2021
@@ -1905,7 +1905,10 @@ mkTxMeta ti' blockHeader wState tx cs expiry =
, direction = if amtInps > amtOuts then Outgoing else Incoming
, slotNo = blockHeader ^. #slotNo
, blockHeight = blockHeader ^. #blockHeight
, amount = Quantity $ distance amtInps amtOuts
-- fixme: ADP-347 calculate asset balance too
Copy link
Member

Choose a reason for hiding this comment

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

The amount should remain unchanged and ada-only. We will not be providing this information for assets. It is less useful for assets since fees can only be paid in Ada. What assets are being sent can be figured out from the resolved inputs and outputs fully.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK then. Nonetheless I thought it would be good to calculate a total of assets moved in or out of the wallet by the transaction. In mkTxMeta it's easier to know which tx outputs are "ours". Resolved inputs and outputs show the address only.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This feature is tracked by ADP-683.

@@ -376,7 +378,9 @@ prefilterBlock b u0 = runState $ do
, direction = dir
, slotNo = b ^. #header . #slotNo
, blockHeight = b ^. #header . #blockHeight
, amount = Quantity amt
-- fixme: ADP-347
-- fixme: why on earth do we have both Coin and Quantity "lovelace" Natural?
Copy link
Member

Choose a reason for hiding this comment

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

At the very beginning, the Quantity type was only used in the Api as a way to generate pretty JSON. It has one day escaped its cage and spread over the rest of the codebase as a quick replacement for newtypes over numeric types.. So in some places we have Coin, in some Word64, in some Quantity "lovelace" Natural or Quantity "lovelace" Word64 or also Fee which is very much the same thing as Coin.

instance FromJSON (ApiT W.TokenMap) where
parseJSON = fmap (ApiT . W.getFlat) . parseJSON
instance ToJSON (ApiT W.TokenMap) where
toJSON = toJSON . W.Flat . getApiT
Copy link
Member

Choose a reason for hiding this comment

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

Hi @rvl

I'm just curious about the reason for choosing Flat here. Is there a reason to not use Nested?

(From recollection, the Daedalus team indicated that they preferred a nested representation, where assets were grouped by token policy.)

Apologies if I'm missing some context here which would make the reason for this choice obvious.

Copy link
Member

Choose a reason for hiding this comment

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

Nesting makes it more complicated to process. The fact that there's a hierarchy policy_id -> asset_name is actually an implementation detail. End-users care about tokens, and tokens are uniquely identified by a pair (policy_id, asset_name). Tokens with different names are different which is why in the end, a flat views of assets makes makes more sense in the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Best place to comment on the API would be #2446. But it's one line of javascript to convert between flat and nested. EIther way would probably be fine.

@@ -81,3 +83,11 @@ instance ToText TokenName where

instance FromText TokenName where
fromText = pure . UnsafeTokenName
Copy link
Member

Choose a reason for hiding this comment

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

Related question: are we planning to change the type of TokenName from Text to ByteString (or perhaps ShortByteString), given that it can be any sequence of bytes between 0 bytes and 32 bytes in length (and therefore might not be representable by Text).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course, because Text is the wrong type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@@ -2044,6 +2084,7 @@ mkApiTransaction ti txid mfee ins outs ws (meta, timestamp) txMeta setTimeRefere
, inputs = [ApiTxInput (fmap toAddressAmount o) (ApiT i) | (i, o) <- ins]
, outputs = toAddressAmount <$> outs
, withdrawals = mkApiWithdrawal @n <$> Map.toList ws
, forge = mempty -- fixme: ADP-604
Copy link
Member

Choose a reason for hiding this comment

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

Question: Should this be mint? And do we need to support this particular field as part of ADP-604?

Suggested change
, forge = mempty -- fixme: ADP-604
, mint = mempty -- fixme: ADP-604

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok thanks - i'll use "mint" to match the terminology from Cardano.Api.
The API field is a placeholder to help Daedalus team with integration.

The correct place to review these names is in #2446 - because ApiTransaction just mirrors what's in swagger.yaml.

@rvl rvl force-pushed the rvl/adp-604/api-ma branch 2 times, most recently from f4c5489 to fc15718 Compare January 19, 2021 13:01
-> Handler [ApiAsset]
listAssets ctx (ApiT wid) =
Handler $ ExceptT $ withDatabase df wid $ \db -> runHandler $ do
let wrk = hoistResource db (MsgFromWorker wid) ctx
Copy link
Member

Choose a reason for hiding this comment

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

Use withWorkerCtx instead:

withWorkerCtx @_ @s @k ctx wid liftE liftE $ \wrk -> liftHandler $
        W.readWallet @_ @s @k wrk wid 

Copy link
Member

Choose a reason for hiding this comment

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

withWorkerCtx comes with some error handling that is common to all wallets accesses.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, that's better.

Nothing -> case T.decodeUtf8' assetNameBytes of
Right "" -> hexText $ getHash $ W.unTokenPolicyId pid
Right text -> text
Left _ -> hexText assetNameBytes
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure to get the rationale for the Nothing branch in this function. The assetName isn't something that is supposed to be user-facing or displayed to user.

I'd suggest to leave it null if there's no metadata (that is, simply leave it as metadata.name) or, to go with your first idea in the cryptpad spec: a bech32 encoded string comprised of the policyId | assetName, possibly hashed. Though, it's not really anymore displayable than the policyId and assetName.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok I have removed displayName for the time being.

AddressAmount
<$> v .: "address"
<*> (v .: "amount" >>= validateCoin)
<*> v .:? "assets" .!= mempty
Copy link
Member

Choose a reason for hiding this comment

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

👍

{ status = InLedger
, direction = dir
, slotNo = b ^. #header . #slotNo
, blockHeight = b ^. #header . #blockHeight
, amount = Quantity amt
, amount = amount
Copy link
Member

@KtorZ KtorZ Jan 20, 2021

Choose a reason for hiding this comment

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

This should be okay-ish for Ada / Coin because we know that the max supply is 42B. But we should generally be careful when summing token quantities. Even if each token individually fit in a Word64, their sum might not. So for anything that regards folds or sums, we should stick to Natural IMO.

Copy link
Member

Choose a reason for hiding this comment

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

At the very least, if changing this to Coin, I'd like to leave a note in the code about exactly this.

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 have added a note to Coin.hs.

In Model.hs, any sums are limited to a single transaction, so it's all fine for ada amounts, and token amounts use Natural.

I would not be against changing newtype Coin to use Natural or Integer, just to remove all doubt about overflow.

@rvl rvl force-pushed the rvl/adp-604/api-ma branch 2 times, most recently from f3d8bef to fc978b5 Compare January 22, 2021 22:36
jonathanknowles added a commit that referenced this pull request Jan 25, 2021
@rvl rvl marked this pull request as ready for review January 25, 2021 08:59
@rvl
Copy link
Contributor Author

rvl commented Jan 25, 2021

Rebased and squashed...

bors r+

iohk-bors bot added a commit that referenced this pull request Jan 25, 2021
2447: Multi-asset API extensions r=rvl a=rvl

### Issue Number

ADP-604

### Overview

Starting implementation of API spec in #2446.

- [x] listAssets endpoint
- [x] getAsset endpoint
- [x] Added `ApiWalletAssetsBalance` which provides the balance of non-ade assets.
- [x] Removed WalletBalance from Primitive.Types and replaced `ApiT WalletBalance` with `ApiWalletBalance`.
- [ ] Some kind of integration test for these endpoints.
- [x] Remove `Quantity "lovelace" a` where possible, except for the API.
- [x] TokenName inner type is now ByteString, add length validation.

### Comments
- This branch is based on top of the PR #2446 branch.



Co-authored-by: KtorZ <matthias.benkort@gmail.com>
Co-authored-by: Rodney Lorrimar <rodney.lorrimar@iohk.io>
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 25, 2021

Build failed:

Failures:

  src/Test/Integration/Scenario/API/Shelley/StakePools.hs:419:5: 
  1) API Specifications, SHELLEY_STAKE_POOLS, STAKE_POOLS_JOIN_02 - Cannot join already joined stake pool
       uncaught exception: RequestException
       DecodeFailure "{\"code\":\"network_query_failed\",\"message\":\"Unable to query the ledger at the moment. This error has been logged. Trying again in a bit might work.\"}"

  To rerun use: --match "/API Specifications/SHELLEY_STAKE_POOLS/STAKE_POOLS_JOIN_02 - Cannot join already joined stake pool/"

Randomized with seed 590232999

Finished in 998.9463 seconds
711 examples, 1 failure, 12 pending
builder for '/nix/store/6i117xq699x522a2z46950f1qn23425z-cardano-wallet-test-integration-2021.1.12-x86_64-unknown-linux-musl-check-x86_64-unknown-linux-musl.drv' failed with exit code 1

#2320

@Anviking
Copy link
Collaborator

bors r+

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 25, 2021

Merge conflict.

@Anviking
Copy link
Collaborator

rebased

bors r+

iohk-bors bot added a commit that referenced this pull request Jan 25, 2021
2447: Multi-asset API extensions r=Anviking a=rvl

### Issue Number

ADP-604

### Overview

Starting implementation of API spec in #2446.

- [x] listAssets endpoint
- [x] getAsset endpoint
- [x] Added `ApiWalletAssetsBalance` which provides the balance of non-ade assets.
- [x] Removed WalletBalance from Primitive.Types and replaced `ApiT WalletBalance` with `ApiWalletBalance`.
- [ ] Some kind of integration test for these endpoints.
- [x] Remove `Quantity "lovelace" a` where possible, except for the API.
- [x] TokenName inner type is now ByteString, add length validation.

### Comments
- This branch is based on top of the PR #2446 branch.



Co-authored-by: KtorZ <matthias.benkort@gmail.com>
Co-authored-by: Rodney Lorrimar <rodney.lorrimar@iohk.io>
jonathanknowles added a commit that referenced this pull request Jan 25, 2021
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 25, 2021

Build failed:

Failures:

  src/Test/Integration/Framework/DSL.hs:780:11:
  1) API Specifications, SHELLEY_TRANSACTIONS, TRANS_ESTIMATE_03b - we see result when we can't cover fee (with withdrawal)
       0 does not satisfy (> 0)
       While verifying (Status {statusCode = 200, statusMessage = "OK"},Right (ApiWallet {id = ApiT {getApiT = WalletId {getWalletId = e262ca635d5bafdeaeaf2c58f84027cf38e9c1e6}}, addressPoolGap = ApiT {getApiT = AddressPoolGap {getAddressPoolGap = 20}}, balance = ApiWalletBalance {available = Quantity {getQuantity = 100000000000}, total = Quantity {getQuantity = 100000000000}, reward = Quantity {getQuantity = 0}}, assets = ApiWalletAssetsBalance {available = ApiT {getApiT = TokenMap (fromList [])}, total = ApiT {getApiT = TokenMap (fromList [])}}, delegation = ApiWalletDelegation {active = ApiWalletDelegationNext {status = NotDelegating, target = Nothing, changesAt = Nothing}, next = []}, name = ApiT {getApiT = WalletName {getWalletName = "MIR Wallet"}}, passphrase = Just (ApiWalletPassphraseInfo {lastUpdatedAt = 2021-01-25 12:31:12.807534 UTC}), state = ApiT {getApiT = Ready}, tip = ApiBlockReference {absoluteSlotNumber = ApiT {getApiT = SlotNo 2899}, slotId = ApiSlotId {epochNumber = ApiT {getApiT = EpochNo {unEpochNo = 57}}, slotNumber = ApiT {getApiT = SlotInEpoch {unSlotInEpoch = 49}}}, time = 2021-01-25 12:32:50.8 UTC, block = ApiBlockInfo {height = Quantity {getQuantity = 1399}}}}))
       Waited longer than 90s to resolve action: "MIR wallet: wallet is 100% synced ".

  To rerun use: --match "/API Specifications/SHELLEY_TRANSACTIONS/TRANS_ESTIMATE_03b - we see result when we can't cover fee (with withdrawal)/"

  src/Test/Integration/Framework/DSL.hs:780:11:
  2) API Specifications, SHELLEY_TRANSACTIONS, SHELLEY_TX_REDEEM_01 - Can redeem rewards from self
       0 does not satisfy (> 0)
       While verifying (Status {statusCode = 200, statusMessage = "OK"},Right (ApiWallet {id = ApiT {getApiT = WalletId {getWalletId = 6a992466e5ec9d721993925a313e7898bc076e87}}, addressPoolGap = ApiT {getApiT = AddressPoolGap {getAddressPoolGap = 20}}, balance = ApiWalletBalance {available = Quantity {getQuantity = 100000000000}, total = Quantity {getQuantity = 100000000000}, reward = Quantity {getQuantity = 0}}, assets = ApiWalletAssetsBalance {available = ApiT {getApiT = TokenMap (fromList [])}, total = ApiT {getApiT = TokenMap (fromList [])}}, delegation = ApiWalletDelegation {active = ApiWalletDelegationNext {status = NotDelegating, target = Nothing, changesAt = Nothing}, next = []}, name = ApiT {getApiT = WalletName {getWalletName = "MIR Wallet"}}, passphrase = Just (ApiWalletPassphraseInfo {lastUpdatedAt = 2021-01-25 12:32:25.326288 UTC}), state = ApiT {getApiT = Ready}, tip = ApiBlockReference {absoluteSlotNumber = ApiT {getApiT = SlotNo 3251}, slotId = ApiSlotId {epochNumber = ApiT {getApiT = EpochNo {unEpochNo = 65}}, slotNumber = ApiT {getApiT = SlotInEpoch {unSlotInEpoch = 1}}}, time = 2021-01-25 12:34:01.2 UTC, block = ApiBlockInfo {height = Quantity {getQuantity = 1570}}}}))
       Waited longer than 90s to resolve action: "MIR wallet: wallet is 100% synced ".

Think this is reward-related:

#2467

@Anviking
Copy link
Collaborator

bors r+

iohk-bors bot added a commit that referenced this pull request Jan 25, 2021
2447: Multi-asset API extensions r=Anviking a=rvl

### Issue Number

ADP-604

### Overview

Starting implementation of API spec in #2446.

- [x] listAssets endpoint
- [x] getAsset endpoint
- [x] Added `ApiWalletAssetsBalance` which provides the balance of non-ade assets.
- [x] Removed WalletBalance from Primitive.Types and replaced `ApiT WalletBalance` with `ApiWalletBalance`.
- [ ] Some kind of integration test for these endpoints.
- [x] Remove `Quantity "lovelace" a` where possible, except for the API.
- [x] TokenName inner type is now ByteString, add length validation.

### Comments
- This branch is based on top of the PR #2446 branch.



Co-authored-by: KtorZ <matthias.benkort@gmail.com>
Co-authored-by: Rodney Lorrimar <rodney.lorrimar@iohk.io>
@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 25, 2021

Build failed:

  test/unit/Cardano/Wallet/DB/StateMachine.hs:1329:28:
  1) Cardano.Wallet.DB.Sqlite, Validate generators & shrinkers, Shrinker for CreateWallet
       Timed out.

#2393

@KtorZ
Copy link
Member

KtorZ commented Jan 25, 2021

bors retry

@iohk-bors
Copy link
Contributor

iohk-bors bot commented Jan 25, 2021

Build succeeded:

@iohk-bors iohk-bors bot merged commit df25b2c into master Jan 25, 2021
@iohk-bors iohk-bors bot deleted the rvl/adp-604/api-ma branch January 25, 2021 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ADDING FEATURE Mark a PR as adding a new feature, for auto-generated CHANGELOG
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants