From d9be4b1f78df731a0c229c2f367e5258822bd2dd Mon Sep 17 00:00:00 2001 From: Brandon Kase Date: Wed, 9 Sep 2020 17:17:08 +0300 Subject: [PATCH 1/2] WIP upgrading 1.4.4 --- src/lib/rosetta_models/coin_change.ml | 2 +- src/lib/rosetta_models/coin_identifier.ml | 2 +- .../rosetta_models/construction_derive_response.ml | 7 ++++--- .../rosetta_models/construction_metadata_request.ml | 9 +++++---- .../rosetta_models/construction_parse_response.ml | 7 ++++--- .../rosetta_models/construction_payloads_request.ml | 9 +++++---- .../construction_preprocess_response.ml | 9 +++++---- src/lib/rosetta_models/signing_payload.ml | 13 +++++++------ 8 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/lib/rosetta_models/coin_change.ml b/src/lib/rosetta_models/coin_change.ml index a8f21df600b..43686388a61 100644 --- a/src/lib/rosetta_models/coin_change.ml +++ b/src/lib/rosetta_models/coin_change.ml @@ -7,7 +7,7 @@ *) type t = {coin_identifier: Coin_identifier.t; coin_action: Enums.coinaction} -[@@deriving yojson {strict= false}, show, eq] +[@@deriving yojson {strict= false}, show] (** CoinChange is used to represent a change in state of a some coin identified by a coin_identifier. This object is part of the Operation model and must be populated for UTXO-based blockchains. Coincidentally, this abstraction of UTXOs allows for supporting both account-based transfers and UTXO-based transfers on the same blockchain (when a transfer is account-based, don't populate this model). *) let create (coin_identifier : Coin_identifier.t) diff --git a/src/lib/rosetta_models/coin_identifier.ml b/src/lib/rosetta_models/coin_identifier.ml index 607c6f43ebe..b617799caf7 100644 --- a/src/lib/rosetta_models/coin_identifier.ml +++ b/src/lib/rosetta_models/coin_identifier.ml @@ -9,7 +9,7 @@ type t = { (* Identifier should be populated with a globally unique identifier of a Coin. In Bitcoin, this identifier would be transaction_hash:index. *) identifier: string } -[@@deriving yojson {strict= false}, show, eq] +[@@deriving yojson {strict= false}, show] (** CoinIdentifier uniquely identifies a Coin. *) let create (identifier : string) : t = {identifier} diff --git a/src/lib/rosetta_models/construction_derive_response.ml b/src/lib/rosetta_models/construction_derive_response.ml index 58b0d1c4cbf..c16f34cb157 100644 --- a/src/lib/rosetta_models/construction_derive_response.ml +++ b/src/lib/rosetta_models/construction_derive_response.ml @@ -7,10 +7,11 @@ *) type t = - { (* Address in network-specific format. *) - address: string + { (* [DEPRECATED by `account_identifier` in `v1.4.4`] Address in network-specific format. *) + address: string option [@default None] + ; account_identifier: Account_identifier.t option [@default None] ; metadata: Yojson.Safe.t option [@default None] } [@@deriving yojson {strict= false}, show] (** ConstructionDeriveResponse is returned by the `/construction/derive` endpoint. *) -let create (address : string) : t = {address; metadata= None} +let create () : t = {address= None; account_identifier= None; metadata= None} diff --git a/src/lib/rosetta_models/construction_metadata_request.ml b/src/lib/rosetta_models/construction_metadata_request.ml index ae595e32f60..aa5fe57481e 100644 --- a/src/lib/rosetta_models/construction_metadata_request.ml +++ b/src/lib/rosetta_models/construction_metadata_request.ml @@ -3,16 +3,17 @@ * * Generated by: https://openapi-generator.tech * - * Schema Construction_metadata_request.t : A ConstructionMetadataRequest is utilized to get information required to construct a transaction. The Options object used to specify which metadata to return is left purposely unstructured to allow flexibility for implementers. + * Schema Construction_metadata_request.t : A ConstructionMetadataRequest is utilized to get information required to construct a transaction. The Options object used to specify which metadata to return is left purposely unstructured to allow flexibility for implementers. Optionally, the request can also include an array of PublicKeys associated with the AccountIdentifiers returned in ConstructionPreprocessResponse. *) type t = { network_identifier: Network_identifier.t ; (* Some blockchains require different metadata for different types of transaction construction (ex: delegation versus a transfer). Instead of requiring a blockchain node to return all possible types of metadata for construction (which may require multiple node fetches), the client can populate an options object to limit the metadata returned to only the subset required. *) - options: Yojson.Safe.t } + options: Yojson.Safe.t + ; public_keys: Public_key.t list } [@@deriving yojson {strict= false}, show] -(** A ConstructionMetadataRequest is utilized to get information required to construct a transaction. The Options object used to specify which metadata to return is left purposely unstructured to allow flexibility for implementers. *) +(** A ConstructionMetadataRequest is utilized to get information required to construct a transaction. The Options object used to specify which metadata to return is left purposely unstructured to allow flexibility for implementers. Optionally, the request can also include an array of PublicKeys associated with the AccountIdentifiers returned in ConstructionPreprocessResponse. *) let create (network_identifier : Network_identifier.t) (options : Yojson.Safe.t) : t = - {network_identifier; options} + {network_identifier; options; public_keys= []} diff --git a/src/lib/rosetta_models/construction_parse_response.ml b/src/lib/rosetta_models/construction_parse_response.ml index 5ff2e7c008f..1222170b79d 100644 --- a/src/lib/rosetta_models/construction_parse_response.ml +++ b/src/lib/rosetta_models/construction_parse_response.ml @@ -8,11 +8,12 @@ type t = { operations: Operation.t list - ; (* All signers of a particular transaction. If the transaction is unsigned, it should be empty. *) + ; (* [DEPRECATED by `account_identifier_signers` in `v1.4.4`] All signers (addresses) of a particular transaction. If the transaction is unsigned, it should be empty. *) signers: string list + ; account_identifier_signers: Account_identifier.t list ; metadata: Yojson.Safe.t option [@default None] } [@@deriving yojson {strict= false}, show] (** ConstructionParseResponse contains an array of operations that occur in a transaction blob. This should match the array of operations provided to `/construction/preprocess` and `/construction/payloads`. *) -let create (operations : Operation.t list) (signers : string list) : t = - {operations; signers; metadata= None} +let create (operations : Operation.t list) : t = + {operations; signers= []; account_identifier_signers= []; metadata= None} diff --git a/src/lib/rosetta_models/construction_payloads_request.ml b/src/lib/rosetta_models/construction_payloads_request.ml index 5ed25d3f000..e707df360e2 100644 --- a/src/lib/rosetta_models/construction_payloads_request.ml +++ b/src/lib/rosetta_models/construction_payloads_request.ml @@ -3,16 +3,17 @@ * * Generated by: https://openapi-generator.tech * - * Schema Construction_payloads_request.t : ConstructionPayloadsRequest is the request to `/construction/payloads`. It contains the network, a slice of operations, and arbitrary metadata that was returned by the call to `/construction/metadata`. + * Schema Construction_payloads_request.t : ConstructionPayloadsRequest is the request to `/construction/payloads`. It contains the network, a slice of operations, and arbitrary metadata that was returned by the call to `/construction/metadata`. Optionally, the request can also include an array of PublicKeys associated with the AccountIdentifiers returned in ConstructionPreprocessResponse. *) type t = { network_identifier: Network_identifier.t ; operations: Operation.t list - ; metadata: Yojson.Safe.t option [@default None] } + ; metadata: Yojson.Safe.t option [@default None] + ; public_keys: Public_key.t list } [@@deriving yojson {strict= false}, show] -(** ConstructionPayloadsRequest is the request to `/construction/payloads`. It contains the network, a slice of operations, and arbitrary metadata that was returned by the call to `/construction/metadata`. *) +(** ConstructionPayloadsRequest is the request to `/construction/payloads`. It contains the network, a slice of operations, and arbitrary metadata that was returned by the call to `/construction/metadata`. Optionally, the request can also include an array of PublicKeys associated with the AccountIdentifiers returned in ConstructionPreprocessResponse. *) let create (network_identifier : Network_identifier.t) (operations : Operation.t list) : t = - {network_identifier; operations; metadata= None} + {network_identifier; operations; metadata= None; public_keys= []} diff --git a/src/lib/rosetta_models/construction_preprocess_response.ml b/src/lib/rosetta_models/construction_preprocess_response.ml index 6531b93ffa7..999e7edd93c 100644 --- a/src/lib/rosetta_models/construction_preprocess_response.ml +++ b/src/lib/rosetta_models/construction_preprocess_response.ml @@ -3,13 +3,14 @@ * * Generated by: https://openapi-generator.tech * - * Schema Construction_preprocess_response.t : ConstructionPreprocessResponse contains the request that will be sent directly to `/construction/metadata`. If it is not necessary to make a request to `/construction/metadata`, options should be null. + * Schema Construction_preprocess_response.t : ConstructionPreprocessResponse contains `options` that will be sent unmodified to `/construction/metadata`. If it is not necessary to make a request to `/construction/metadata`, `options` should be omitted. Some blockchains require the PublicKey of particular AccountIdentifiers to construct a valid transaction. To fetch these PublicKeys, populate `required_public_keys` with the AccountIdentifiers associated with the desired PublicKeys. If it is not necessary to retrieve any PublicKeys for construction, `required_public_keys` should be omitted. *) type t = { (* The options that will be sent directly to `/construction/metadata` by the caller. *) - options: Yojson.Safe.t option [@default None] } + options: Yojson.Safe.t option [@default None] + ; required_public_keys: Account_identifier.t list } [@@deriving yojson {strict= false}, show] -(** ConstructionPreprocessResponse contains the request that will be sent directly to `/construction/metadata`. If it is not necessary to make a request to `/construction/metadata`, options should be null. *) -let create () : t = {options= None} +(** ConstructionPreprocessResponse contains `options` that will be sent unmodified to `/construction/metadata`. If it is not necessary to make a request to `/construction/metadata`, `options` should be omitted. Some blockchains require the PublicKey of particular AccountIdentifiers to construct a valid transaction. To fetch these PublicKeys, populate `required_public_keys` with the AccountIdentifiers associated with the desired PublicKeys. If it is not necessary to retrieve any PublicKeys for construction, `required_public_keys` should be omitted. *) +let create () : t = {options= None; required_public_keys= []} diff --git a/src/lib/rosetta_models/signing_payload.ml b/src/lib/rosetta_models/signing_payload.ml index 330ab1bdcb6..8d5f6c31897 100644 --- a/src/lib/rosetta_models/signing_payload.ml +++ b/src/lib/rosetta_models/signing_payload.ml @@ -3,16 +3,17 @@ * * Generated by: https://openapi-generator.tech * - * Schema Signing_payload.t : SigningPayload is signed by the client with the keypair associated with an address using the specified SignatureType. SignatureType can be optionally populated if there is a restriction on the signature scheme that can be used to sign the payload. + * Schema Signing_payload.t : SigningPayload is signed by the client with the keypair associated with an AccountIdentifier using the specified SignatureType. SignatureType can be optionally populated if there is a restriction on the signature scheme that can be used to sign the payload. *) type t = - { (* The network-specific address of the account that should sign the payload. *) - address: string + { (* [DEPRECATED by `account_identifier` in `v1.4.4`] The network-specific address of the account that should sign the payload. *) + address: string option [@default None] + ; account_identifier: Account_identifier.t option [@default None] ; hex_bytes: string ; signature_type: Enums.signaturetype option [@default None] } [@@deriving yojson {strict= false}, show] -(** SigningPayload is signed by the client with the keypair associated with an address using the specified SignatureType. SignatureType can be optionally populated if there is a restriction on the signature scheme that can be used to sign the payload. *) -let create (address : string) (hex_bytes : string) : t = - {address; hex_bytes; signature_type= None} +(** SigningPayload is signed by the client with the keypair associated with an AccountIdentifier using the specified SignatureType. SignatureType can be optionally populated if there is a restriction on the signature scheme that can be used to sign the payload. *) +let create (hex_bytes : string) : t = + {address= None; account_identifier= None; hex_bytes; signature_type= None} From 65570170c341709be847625e5dd4d33919851e21 Mon Sep 17 00:00:00 2001 From: Brandon Kase Date: Thu, 10 Sep 2020 01:02:58 +0300 Subject: [PATCH 2/2] Finishes updating rosetta for 1.4.4 Compiles and start.sh passes with rosetta-cli check:data --- src/app/rosetta/lib/construction.ml | 40 ++++++++++++++--------- src/app/rosetta/lib/network.ml | 4 +-- src/app/rosetta/test-agent/agent.ml | 28 ++++++++-------- src/app/rosetta/test-agent/offline.ml | 8 +++-- src/app/rosetta/test-agent/peek.ml | 5 ++- src/lib/rosetta_models/coin_change.ml | 2 +- src/lib/rosetta_models/coin_identifier.ml | 2 +- 7 files changed, 53 insertions(+), 36 deletions(-) diff --git a/src/app/rosetta/lib/construction.ml b/src/app/rosetta/lib/construction.ml index d6458cbcdfd..892f37d031d 100644 --- a/src/app/rosetta/lib/construction.ml +++ b/src/app/rosetta/lib/construction.ml @@ -174,11 +174,12 @@ module Derive = struct end module Impl (M : Monad_fail.S) = struct - (* TODO: Don't assume req.metadata is a token_id without checking *) + module Token_id_decode = Amount_of.Token_id.T (M) + let handle ~(env : Env.T(M).t) (req : Construction_derive_request.t) = let open M.Let_syntax in (* TODO: Verify curve-type is tweedle *) - let%map pk = + let%bind pk = let pk_or_error = try Ok (Rosetta_coding.Coding.to_public_key req.public_key.hex_bytes) with exn -> Error (Core_kernel.Error.of_exn exn) @@ -188,9 +189,14 @@ module Derive = struct ~f:(fun _ -> Errors.create `Malformed_public_key) pk_or_error in - { Construction_derive_response.address= - Public_key.(compress pk |> Compressed.to_base58_check) - ; metadata= req.metadata } + let%map token_id = Token_id_decode.decode req.metadata in + { Construction_derive_response.address= None + ; account_identifier= + Some + (User_command_info.account_id + (`Pk Public_key.(compress pk |> Compressed.to_base58_check)) + (Option.value ~default:Amount_of.Token_id.default token_id)) + ; metadata= None } end module Real = Impl (Deferred.Result) @@ -317,7 +323,8 @@ module Preprocess = struct (Options.to_json { Options.sender= pk ; token_id= partial_user_command.User_command_info.Partial.token - }) } + }) + ; required_public_keys= [] } end module Real = Impl (Deferred.Result) @@ -392,11 +399,12 @@ module Payloads = struct { Construction_payloads_response.unsigned_transaction= unsigned_transaction_string ; payloads= - [ { Signing_payload.address= - (let (`Pk pk) = - partial_user_command.User_command_info.Partial.source - in - pk) + [ { Signing_payload.address= None + ; account_identifier= + Some + (User_command_info.account_id + partial_user_command.User_command_info.Partial.source + partial_user_command.User_command_info.Partial.token) ; hex_bytes= pk ; signature_type= Some "schnorr_poseidon" } ] } end @@ -479,7 +487,7 @@ module Parse = struct try M.return (Yojson.Safe.from_string req.transaction) with _ -> M.fail (Errors.create (`Json_parse None)) in - let%map operations, `Pk signer_pk = + let%map operations, account_identifier_signers = match req.signed with | true -> let%map signed_transaction = @@ -491,7 +499,8 @@ module Parse = struct in ( User_command_info.to_operations ~failure_status:None signed_transaction.command - , signed_transaction.command.source ) + , [ User_command_info.account_id signed_transaction.command.source + signed_transaction.command.token ] ) | false -> let%map unsigned_transaction = Transaction.Unsigned.Rendered.of_yojson json @@ -502,10 +511,11 @@ module Parse = struct in ( User_command_info.to_operations ~failure_status:None unsigned_transaction.command - , unsigned_transaction.command.source ) + , [] ) in { Construction_parse_response.operations - ; signers= [signer_pk] + ; signers= [] + ; account_identifier_signers ; metadata= None } end diff --git a/src/app/rosetta/lib/network.ml b/src/app/rosetta/lib/network.ml index c083395ee44..e72621e9dfc 100644 --- a/src/app/rosetta/lib/network.ml +++ b/src/app/rosetta/lib/network.ml @@ -440,7 +440,7 @@ module Options = struct ~network_identifier:network.network_identifier in { Network_options_response.version= - Version.create "1.4.2" (Option.value ~default:"unknown" res#version) + Version.create "1.4.4" (Option.value ~default:"unknown" res#version) ; allow= { Allow.operation_statuses= Lazy.force Operation_statuses.all ; operation_types= Lazy.force Operation_types.all @@ -468,7 +468,7 @@ module Options = struct ~actual:(Mock.handle ~env dummy_network_request) ~expected: ( Result.return - @@ { Network_options_response.version= Version.create "1.4.2" "v1.0" + @@ { Network_options_response.version= Version.create "1.4.4" "v1.0" ; allow= { Allow.operation_statuses= Lazy.force Operation_statuses.all ; operation_types= Lazy.force Operation_types.all diff --git a/src/app/rosetta/test-agent/agent.ml b/src/app/rosetta/test-agent/agent.ml index 9247db370ae..3df029121b3 100644 --- a/src/app/rosetta/test-agent/agent.ml +++ b/src/app/rosetta/test-agent/agent.ml @@ -316,7 +316,9 @@ let construction_api_transaction_through_mempool ~logger ~rosetta_uri Offline.Derive.req ~logger ~rosetta_uri ~network_response ~public_key_hex_bytes:keys.public_key_hex_bytes in - let operations = operations derive_res.address in + let operations = + operations (Option.value_exn derive_res.account_identifier) + in let%bind preprocess_res = Offline.Preprocess.req ~logger ~rosetta_uri ~network_response ~max_fee:(Unsigned.UInt64.of_int 100_000_000_000) @@ -357,7 +359,7 @@ let construction_api_transaction_through_mempool ~logger ~rosetta_uri Offline.Combine.req ~logger ~rosetta_uri ~network_response ~signature ~unsigned_transaction:payloads_res.unsigned_transaction ~public_key_hex_bytes:keys.public_key_hex_bytes - ~address:derive_res.address + ~account_id:(Option.value_exn derive_res.account_identifier) in let%bind combine_parse_res = Offline.Parse.req ~logger ~rosetta_uri ~network_response @@ -403,8 +405,8 @@ let construction_api_transaction_through_mempool ~logger ~rosetta_uri let construction_api_payment_through_mempool = construction_api_transaction_through_mempool - ~operations:(fun address -> - Poke.SendTransaction.payment_operations ~from:address + ~operations:(fun account_id -> + Poke.SendTransaction.payment_operations ~from:account_id.address ~fee:(Unsigned.UInt64.of_int 3_000_000_000) ~amount:(Unsigned.UInt64.of_int 10_000_000_000) ~to_:other_pk ) @@ -431,8 +433,8 @@ let construction_api_payment_through_mempool = let construction_api_delegation_through_mempool = construction_api_transaction_through_mempool - ~operations:(fun address -> - Poke.SendTransaction.delegation_operations ~from:address + ~operations:(fun account_id -> + Poke.SendTransaction.delegation_operations ~from:account_id.address ~fee:(Unsigned.UInt64.of_int 5_000_000_000) ~to_:other_pk ) ~operation_expectations: @@ -452,8 +454,8 @@ let construction_api_delegation_through_mempool = let construction_api_create_token_through_mempool = construction_api_transaction_through_mempool - ~operations:(fun address -> - Poke.SendTransaction.create_token_operations ~sender:address + ~operations:(fun account_id -> + Poke.SendTransaction.create_token_operations ~sender:account_id.address ~fee:(Unsigned.UInt64.of_int 5_000_000_000) ) ~operation_expectations: Operation_expectation. @@ -471,8 +473,8 @@ let construction_api_create_token_through_mempool = let construction_api_create_token_account_through_mempool = construction_api_transaction_through_mempool - ~operations:(fun address -> - Poke.SendTransaction.create_token_operations ~sender:address + ~operations:(fun account_id -> + Poke.SendTransaction.create_token_operations ~sender:account_id.address ~fee:(Unsigned.UInt64.of_int 5_000_000_000) ) ~operation_expectations: Operation_expectation. @@ -485,9 +487,9 @@ let construction_api_create_token_account_through_mempool = let construction_api_mint_tokens_through_mempool = construction_api_transaction_through_mempool - ~operations:(fun address -> - Poke.SendTransaction.mint_tokens_operations ~sender:address - ~receiver:address + ~operations:(fun account_id -> + Poke.SendTransaction.mint_tokens_operations ~sender:account_id.address + ~receiver:account_id.address ~amount:(Unsigned.UInt64.of_int 1_000_000_000) ~fee:(Unsigned.UInt64.of_int 3_000_000_000) ) ~operation_expectations: diff --git a/src/app/rosetta/test-agent/offline.ml b/src/app/rosetta/test-agent/offline.ml index 0e9b7734c8e..532adf93451 100644 --- a/src/app/rosetta/test-agent/offline.ml +++ b/src/app/rosetta/test-agent/offline.ml @@ -53,7 +53,8 @@ module Payloads = struct Construction_payloads_request.( { network_identifier= net_id network_response ; operations - ; metadata= Some metadata } + ; metadata= Some metadata + ; public_keys= [] } |> to_yojson) ~path:"construction/payloads" in @@ -83,7 +84,7 @@ module Parse = struct end module Combine = struct - let req ~rosetta_uri ~logger ~unsigned_transaction ~signature ~address + let req ~rosetta_uri ~logger ~unsigned_transaction ~signature ~account_id ~public_key_hex_bytes ~network_response = let%bind r = post ~rosetta_uri ~logger @@ -94,7 +95,8 @@ module Combine = struct ; signatures= [ (* TODO: How important is it to fill in all these details properly? *) { Signature.signing_payload= - { Signing_payload.address + { Signing_payload.account_identifier= Some account_id + ; address= None ; hex_bytes= "TODO" ; signature_type= None } ; public_key= diff --git a/src/app/rosetta/test-agent/peek.ml b/src/app/rosetta/test-agent/peek.ml index 07952e9d9b1..41959b99b65 100644 --- a/src/app/rosetta/test-agent/peek.ml +++ b/src/app/rosetta/test-agent/peek.ml @@ -137,7 +137,10 @@ module Construction = struct post ~rosetta_uri ~logger ~body: Construction_metadata_request.( - {network_identifier= net_id network_response; options} |> to_yojson) + { network_identifier= net_id network_response + ; options + ; public_keys= [] } + |> to_yojson) ~path:"construction/metadata" in Lift.res ~logger res ~of_yojson:Construction_metadata_response.of_yojson diff --git a/src/lib/rosetta_models/coin_change.ml b/src/lib/rosetta_models/coin_change.ml index 43686388a61..a8f21df600b 100644 --- a/src/lib/rosetta_models/coin_change.ml +++ b/src/lib/rosetta_models/coin_change.ml @@ -7,7 +7,7 @@ *) type t = {coin_identifier: Coin_identifier.t; coin_action: Enums.coinaction} -[@@deriving yojson {strict= false}, show] +[@@deriving yojson {strict= false}, show, eq] (** CoinChange is used to represent a change in state of a some coin identified by a coin_identifier. This object is part of the Operation model and must be populated for UTXO-based blockchains. Coincidentally, this abstraction of UTXOs allows for supporting both account-based transfers and UTXO-based transfers on the same blockchain (when a transfer is account-based, don't populate this model). *) let create (coin_identifier : Coin_identifier.t) diff --git a/src/lib/rosetta_models/coin_identifier.ml b/src/lib/rosetta_models/coin_identifier.ml index b617799caf7..607c6f43ebe 100644 --- a/src/lib/rosetta_models/coin_identifier.ml +++ b/src/lib/rosetta_models/coin_identifier.ml @@ -9,7 +9,7 @@ type t = { (* Identifier should be populated with a globally unique identifier of a Coin. In Bitcoin, this identifier would be transaction_hash:index. *) identifier: string } -[@@deriving yojson {strict= false}, show] +[@@deriving yojson {strict= false}, show, eq] (** CoinIdentifier uniquely identifies a Coin. *) let create (identifier : string) : t = {identifier}