diff --git a/Cargo.lock b/Cargo.lock index 28dc1e70dc2fe..3c5bd20b38fd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -260,6 +260,7 @@ dependencies = [ "async-trait", "bcs", "hex", + "indoc", "mime", "move-deps", "poem", @@ -4064,6 +4065,12 @@ dependencies = [ "regex", ] +[[package]] +name = "indoc" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e" + [[package]] name = "inspection-service" version = "0.1.0" diff --git a/api/doc/v1/spec.yaml b/api/doc/v1/spec.yaml index 5c0432e244e64..8b90eb0d5aa23 100644 --- a/api/doc/v1/spec.yaml +++ b/api/doc/v1/spec.yaml @@ -32,15 +32,13 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false - name: ledger_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -122,15 +120,13 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false - name: ledger_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -214,15 +210,13 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false - name: ledger_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -320,15 +314,13 @@ paths: parameters: - name: event_key schema: - type: string - format: EventKey + $ref: "#/components/schemas/EventKey" in: path required: true deprecated: false - name: start schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -419,8 +411,7 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false @@ -432,15 +423,13 @@ paths: deprecated: false - name: field_name schema: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" in: path required: true deprecated: false - name: start schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -600,8 +589,7 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false @@ -613,8 +601,7 @@ paths: deprecated: false - name: ledger_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -696,22 +683,19 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false - name: module_name schema: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" in: path required: true deprecated: false - name: ledger_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -793,15 +777,13 @@ paths: parameters: - name: table_handle schema: - type: string - format: U128 + $ref: "#/components/schemas/U128" in: path required: true deprecated: false - name: ledger_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -889,8 +871,7 @@ paths: parameters: - name: start schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -1073,8 +1054,7 @@ paths: parameters: - name: txn_hash schema: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" in: path required: true deprecated: false @@ -1156,8 +1136,7 @@ paths: parameters: - name: txn_version schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: path required: true deprecated: false @@ -1239,15 +1218,13 @@ paths: parameters: - name: address schema: - type: string - format: Address + $ref: "#/components/schemas/Address" in: path required: true deprecated: false - name: start schema: - type: string - format: U64 + $ref: "#/components/schemas/U64" in: query required: false deprecated: false @@ -1442,8 +1419,7 @@ paths: content: application/json: schema: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" application/x-bcs: schema: type: array @@ -1509,11 +1485,9 @@ components: - authentication_key properties: sequence_number: - type: string - format: U64 + $ref: "#/components/schemas/U64" authentication_key: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" AccountSignature: type: object oneOf: @@ -1544,6 +1518,8 @@ components: type: string example: MultiEd25519Signature - $ref: "#/components/schemas/MultiEd25519Signature" + Address: + type: string AptosError: type: object description: "This is the generic struct we use for all API errors, it contains a string\nmessage and an Aptos API specific error code." @@ -1555,8 +1531,7 @@ components: error_code: $ref: "#/components/schemas/AptosErrorCode" aptos_ledger_version: - type: string - format: U64 + $ref: "#/components/schemas/U64" AptosErrorCode: type: string description: "These codes provide more granular error information beyond just the HTTP\nstatus code of the response." @@ -1589,40 +1564,31 @@ components: - timestamp properties: version: - type: string - format: U64 + $ref: "#/components/schemas/U64" hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" state_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" event_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" gas_used: - type: string - format: U64 + $ref: "#/components/schemas/U64" success: type: boolean vm_status: type: string accumulator_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" changes: type: array items: $ref: "#/components/schemas/WriteSetChange" id: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" epoch: - type: string - format: U64 + $ref: "#/components/schemas/U64" round: - type: string - format: U64 + $ref: "#/components/schemas/U64" events: type: array items: @@ -1632,16 +1598,14 @@ components: items: type: boolean proposer: - type: string - format: Address + $ref: "#/components/schemas/Address" failed_proposer_indices: type: array items: type: integer format: uint32 timestamp: - type: string - format: U64 + $ref: "#/components/schemas/U64" DeleteModule: type: object required: @@ -1650,8 +1614,7 @@ components: - module properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" state_key_hash: type: string module: @@ -1664,8 +1627,7 @@ components: - resource properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" state_key_hash: type: string resource: @@ -1680,11 +1642,9 @@ components: state_key_hash: type: string handle: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" key: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" DirectWriteSet: type: object required: @@ -1706,11 +1666,9 @@ components: - signature properties: public_key: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" signature: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" EncodeSubmissionRequest: type: object required: @@ -1722,27 +1680,21 @@ components: - payload properties: sender: - type: string - format: Address + $ref: "#/components/schemas/Address" sequence_number: - type: string - format: U64 + $ref: "#/components/schemas/U64" max_gas_amount: - type: string - format: U64 + $ref: "#/components/schemas/U64" gas_unit_price: - type: string - format: U64 + $ref: "#/components/schemas/U64" expiration_timestamp_secs: - type: string - format: U64 + $ref: "#/components/schemas/U64" payload: $ref: "#/components/schemas/TransactionPayload" secondary_signers: type: array items: - type: string - format: Address + $ref: "#/components/schemas/Address" Event: type: object required: @@ -1752,15 +1704,14 @@ components: - data properties: key: - type: string - format: EventKey + $ref: "#/components/schemas/EventKey" sequence_number: - type: string - format: U64 + $ref: "#/components/schemas/U64" type: - type: string - format: MoveType + $ref: "#/components/schemas/MoveType" data: {} + EventKey: + type: string GenesisPayload: type: object oneOf: @@ -1795,27 +1746,21 @@ components: - events properties: version: - type: string - format: U64 + $ref: "#/components/schemas/U64" hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" state_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" event_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" gas_used: - type: string - format: U64 + $ref: "#/components/schemas/U64" success: type: boolean vm_status: type: string accumulator_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" changes: type: array items: @@ -1826,6 +1771,13 @@ components: type: array items: $ref: "#/components/schemas/Event" + HashValue: + type: string + HexEncodedBytes: + type: string + example: "0x88fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1" + IdentifierWrapper: + type: string IndexResponse: type: object description: "The struct holding all data returned to the client by the\nindex endpoint (i.e., GET \"/\")." @@ -1841,17 +1793,13 @@ components: type: integer format: uint8 epoch: - type: string - format: U64 + $ref: "#/components/schemas/U64" ledger_version: - type: string - format: U64 + $ref: "#/components/schemas/U64" oldest_ledger_version: - type: string - format: U64 + $ref: "#/components/schemas/U64" ledger_timestamp: - type: string - format: U64 + $ref: "#/components/schemas/U64" node_role: $ref: "#/components/schemas/RoleType" ModuleBundlePayload: @@ -1863,6 +1811,8 @@ components: type: array items: $ref: "#/components/schemas/MoveModuleBytecode" + MoveAbility: + type: string MoveFunction: type: object required: @@ -1874,8 +1824,7 @@ components: - return properties: name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" visibility: $ref: "#/components/schemas/MoveFunctionVisibility" is_entry: @@ -1900,8 +1849,7 @@ components: constraints: type: array items: - type: string - format: MoveAbility + $ref: "#/components/schemas/MoveAbility" MoveFunctionVisibility: type: string enum: @@ -1918,11 +1866,9 @@ components: - structs properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" friends: type: array items: @@ -1941,8 +1887,7 @@ components: - bytecode properties: bytecode: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" abi: $ref: "#/components/schemas/MoveModule" MoveModuleId: @@ -1952,11 +1897,9 @@ components: - name properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" MoveResource: type: object required: @@ -1966,16 +1909,14 @@ components: type: $ref: "#/components/schemas/MoveStructTag" data: - type: string - format: MoveStructValue + $ref: "#/components/schemas/MoveStructValue" MoveScriptBytecode: type: object required: - bytecode properties: bytecode: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" abi: $ref: "#/components/schemas/MoveFunction" MoveStruct: @@ -1988,15 +1929,13 @@ components: - fields properties: name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" is_native: type: boolean abilities: type: array items: - type: string - format: MoveAbility + $ref: "#/components/schemas/MoveAbility" generic_type_params: type: array items: @@ -2012,8 +1951,7 @@ components: - type properties: name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" type: $ref: "#/components/schemas/MoveTypeWrapper" MoveStructGenericTypeParam: @@ -2025,8 +1963,7 @@ components: constraints: type: array items: - type: string - format: MoveAbility + $ref: "#/components/schemas/MoveAbility" is_phantom: type: boolean MoveStructTag: @@ -2038,43 +1975,38 @@ components: - generic_type_params properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" module: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" generic_type_params: type: array items: $ref: "#/components/schemas/MoveTypeWrapper" + MoveStructValue: + type: object + MoveType: + type: object MoveTypeWrapper: type: object anyOf: - - type: string - format: MoveType + - $ref: "#/components/schemas/MoveType" - type: string MoveValue: type: object anyOf: - type: integer format: uint8 - - type: string - format: U64 - - type: string - format: U128 + - $ref: "#/components/schemas/U64" + - $ref: "#/components/schemas/U128" - type: boolean - - type: string - format: Address + - $ref: "#/components/schemas/Address" - type: array items: $ref: "#/components/schemas/MoveValue" - - type: string - format: HexEncodedBytes - - type: string - format: MoveStructValue + - $ref: "#/components/schemas/HexEncodedBytes" + - $ref: "#/components/schemas/MoveStructValue" - type: string MultiAgentSignature: type: object @@ -2088,8 +2020,7 @@ components: secondary_signer_addresses: type: array items: - type: string - format: Address + $ref: "#/components/schemas/Address" secondary_signers: type: array items: @@ -2105,19 +2036,16 @@ components: public_keys: type: array items: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" signatures: type: array items: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" threshold: type: integer format: uint8 bitmap: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" PendingTransaction: type: object required: @@ -2130,23 +2058,17 @@ components: - payload properties: hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" sender: - type: string - format: Address + $ref: "#/components/schemas/Address" sequence_number: - type: string - format: U64 + $ref: "#/components/schemas/U64" max_gas_amount: - type: string - format: U64 + $ref: "#/components/schemas/U64" gas_unit_price: - type: string - format: U64 + $ref: "#/components/schemas/U64" expiration_timestamp_secs: - type: string - format: U64 + $ref: "#/components/schemas/U64" payload: $ref: "#/components/schemas/TransactionPayload" signature: @@ -2165,8 +2087,7 @@ components: module: $ref: "#/components/schemas/MoveModuleId" name: - type: string - format: IdentifierWrapper + $ref: "#/components/schemas/IdentifierWrapper" ScriptFunctionPayload: type: object required: @@ -2179,8 +2100,7 @@ components: type_arguments: type: array items: - type: string - format: MoveType + $ref: "#/components/schemas/MoveType" arguments: type: array items: {} @@ -2196,8 +2116,7 @@ components: type_arguments: type: array items: - type: string - format: MoveType + $ref: "#/components/schemas/MoveType" arguments: type: array items: {} @@ -2208,8 +2127,7 @@ components: - script properties: execute_as: - type: string - format: Address + $ref: "#/components/schemas/Address" script: $ref: "#/components/schemas/ScriptPayload" StateCheckpointTransaction: @@ -2227,34 +2145,27 @@ components: - timestamp properties: version: - type: string - format: U64 + $ref: "#/components/schemas/U64" hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" state_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" event_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" gas_used: - type: string - format: U64 + $ref: "#/components/schemas/U64" success: type: boolean vm_status: type: string accumulator_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" changes: type: array items: $ref: "#/components/schemas/WriteSetChange" timestamp: - type: string - format: U64 + $ref: "#/components/schemas/U64" SubmitTransactionRequest: type: object required: @@ -2267,20 +2178,15 @@ components: - signature properties: sender: - type: string - format: Address + $ref: "#/components/schemas/Address" sequence_number: - type: string - format: U64 + $ref: "#/components/schemas/U64" max_gas_amount: - type: string - format: U64 + $ref: "#/components/schemas/U64" gas_unit_price: - type: string - format: U64 + $ref: "#/components/schemas/U64" expiration_timestamp_secs: - type: string - format: U64 + $ref: "#/components/schemas/U64" payload: $ref: "#/components/schemas/TransactionPayload" signature: @@ -2293,11 +2199,9 @@ components: - key properties: key_type: - type: string - format: MoveType + $ref: "#/components/schemas/MoveType" value_type: - type: string - format: MoveType + $ref: "#/components/schemas/MoveType" key: {} Transaction: type: object @@ -2461,6 +2365,10 @@ components: type: string example: UserTransaction - $ref: "#/components/schemas/UserTransaction" + U128: + type: string + U64: + type: string UserTransaction: type: object required: @@ -2483,46 +2391,35 @@ components: - timestamp properties: version: - type: string - format: U64 + $ref: "#/components/schemas/U64" hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" state_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" event_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" gas_used: - type: string - format: U64 + $ref: "#/components/schemas/U64" success: type: boolean vm_status: type: string accumulator_root_hash: - type: string - format: HashValue + $ref: "#/components/schemas/HashValue" changes: type: array items: $ref: "#/components/schemas/WriteSetChange" sender: - type: string - format: Address + $ref: "#/components/schemas/Address" sequence_number: - type: string - format: U64 + $ref: "#/components/schemas/U64" max_gas_amount: - type: string - format: U64 + $ref: "#/components/schemas/U64" gas_unit_price: - type: string - format: U64 + $ref: "#/components/schemas/U64" expiration_timestamp_secs: - type: string - format: U64 + $ref: "#/components/schemas/U64" payload: $ref: "#/components/schemas/TransactionPayload" signature: @@ -2532,8 +2429,7 @@ components: items: $ref: "#/components/schemas/Event" timestamp: - type: string - format: U64 + $ref: "#/components/schemas/U64" WriteModule: type: object required: @@ -2542,8 +2438,7 @@ components: - data properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" state_key_hash: type: string data: @@ -2556,8 +2451,7 @@ components: - data properties: address: - type: string - format: Address + $ref: "#/components/schemas/Address" state_key_hash: type: string data: @@ -2688,13 +2582,10 @@ components: state_key_hash: type: string handle: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" key: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" value: - type: string - format: HexEncodedBytes + $ref: "#/components/schemas/HexEncodedBytes" externalDocs: url: "https://github.com/aptos-labs/aptos-core" diff --git a/api/goldens/v0/aptos_api__tests__state_test__test_get_account_module.json b/api/goldens/v0/aptos_api__tests__state_test__test_get_account_module.json index f1549b4f72f7f..05abb6fdfc16d 100644 --- a/api/goldens/v0/aptos_api__tests__state_test__test_get_account_module.json +++ b/api/goldens/v0/aptos_api__tests__state_test__test_get_account_module.json @@ -11,14 +11,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&signer" - } + "&signer" ], "return": [ - { - "Parsed": "0x1::guid::GUID" - } + "0x1::guid::GUID" ] }, { @@ -27,17 +23,11 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "address" - }, - { - "Parsed": "u64" - } + "address", + "u64" ], "return": [ - { - "Parsed": "0x1::guid::ID" - } + "0x1::guid::ID" ] }, { @@ -46,17 +36,11 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "address" - }, - { - "Parsed": "&0x1::guid::CreateCapability" - } + "address", + "&0x1::guid::CreateCapability" ], "return": [ - { - "Parsed": "0x1::guid::GUID" - } + "0x1::guid::GUID" ] }, { @@ -65,14 +49,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&0x1::guid::GUID" - } + "&0x1::guid::GUID" ], "return": [ - { - "Parsed": "u64" - } + "u64" ] }, { @@ -81,14 +61,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&0x1::guid::GUID" - } + "&0x1::guid::GUID" ], "return": [ - { - "Parsed": "address" - } + "address" ] }, { @@ -97,17 +73,11 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&0x1::guid::GUID" - }, - { - "Parsed": "&0x1::guid::ID" - } + "&0x1::guid::GUID", + "&0x1::guid::ID" ], "return": [ - { - "Parsed": "bool" - } + "bool" ] }, { @@ -116,14 +86,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&signer" - } + "&signer" ], "return": [ - { - "Parsed": "0x1::guid::CreateCapability" - } + "0x1::guid::CreateCapability" ] }, { @@ -132,14 +98,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "address" - } + "address" ], "return": [ - { - "Parsed": "u64" - } + "u64" ] }, { @@ -148,14 +110,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&0x1::guid::GUID" - } + "&0x1::guid::GUID" ], "return": [ - { - "Parsed": "0x1::guid::ID" - } + "0x1::guid::ID" ] }, { @@ -164,14 +122,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&0x1::guid::ID" - } + "&0x1::guid::ID" ], "return": [ - { - "Parsed": "u64" - } + "u64" ] }, { @@ -180,14 +134,10 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&0x1::guid::ID" - } + "&0x1::guid::ID" ], "return": [ - { - "Parsed": "address" - } + "address" ] }, { @@ -196,9 +146,7 @@ "is_entry": false, "generic_type_params": [], "params": [ - { - "Parsed": "&signer" - } + "&signer" ], "return": [] } @@ -216,9 +164,7 @@ "fields": [ { "name": "addr", - "type": { - "Parsed": "address" - } + "type": "address" } ] }, @@ -233,9 +179,7 @@ "fields": [ { "name": "id", - "type": { - "Parsed": "0x1::guid::ID" - } + "type": "0x1::guid::ID" } ] }, @@ -249,9 +193,7 @@ "fields": [ { "name": "counter", - "type": { - "Parsed": "u64" - } + "type": "u64" } ] }, @@ -267,15 +209,11 @@ "fields": [ { "name": "creation_num", - "type": { - "Parsed": "u64" - } + "type": "u64" }, { "name": "addr", - "type": { - "Parsed": "address" - } + "type": "address" } ] } diff --git a/api/goldens/v0/aptos_api__tests__transactions_test__test_get_transaction_by_hash.json b/api/goldens/v0/aptos_api__tests__transactions_test__test_get_transaction_by_hash.json new file mode 100644 index 0000000000000..aba4c2218eaee --- /dev/null +++ b/api/goldens/v0/aptos_api__tests__transactions_test__test_get_transaction_by_hash.json @@ -0,0 +1,109 @@ +[ + { + "type": "write_resource", + "address": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "state_key_hash": "", + "data": { + "type": "0x1::account::Account", + "data": { + "authentication_key": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "coin_register_events": { + "counter": "1", + "guid": { + "id": { + "addr": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "creation_num": "0" + } + } + }, + "self_address": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "sequence_number": "1" + } + } + }, + { + "type": "write_resource", + "address": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "state_key_hash": "", + "data": { + "type": "0x1::guid::Generator", + "data": { + "counter": "6" + } + } + }, + { + "type": "write_resource", + "address": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "state_key_hash": "", + "data": { + "type": "0x1::token::Collections", + "data": { + "burn_capabilities": { + "handle": "168667328656111253118486712107912293335", + "length": "0" + }, + "collections": { + "handle": "289233980808638807779268266229635965202", + "length": "1" + }, + "create_collection_events": { + "counter": "1", + "guid": { + "id": { + "addr": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "creation_num": "3" + } + } + }, + "create_token_events": { + "counter": "0", + "guid": { + "id": { + "addr": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "creation_num": "4" + } + } + }, + "mint_capabilities": { + "handle": "320092724183974385308123126434592627346", + "length": "0" + }, + "mint_token_events": { + "counter": "0", + "guid": { + "id": { + "addr": "0x34bf7e2d17674feb234371a7ea58efd715f0e56ba20ebf13789480d9d643afaf", + "creation_num": "5" + } + } + }, + "token_data": { + "handle": "47387505257877758666710590976362900572", + "length": "0" + } + } + } + }, + { + "type": "write_table_item", + "state_key_hash": "", + "handle": "0xd998703ebdf87ff34cb87c80905b6112", + "key": "0x076d79206e616d65", + "value": "0x12617765736f6d6520636f6c6c656374696f6e076d79206e616d6520687474703a2f2f6170746f736c6162732e636f6d2f69666f6e6c792e6a736f6e000000000000000000", + "data": { + "key": "my name", + "key_type": "0x1::string::String", + "value": { + "count": "0", + "description": "awesome collection", + "maximum": { + "vec": [] + }, + "name": "my name", + "uri": "http://aptoslabs.com/ifonly.json" + }, + "value_type": "0x1::token::Collection" + } + } +] diff --git a/api/goldens/v1/aptos_api__tests__accounts_test__test_get_account_resources_returns_empty_array_for_account_has_no_resources.json b/api/goldens/v1/aptos_api__tests__accounts_test__test_get_account_resources_returns_empty_array_for_account_has_no_resources.json index d345717b50d26..fe78cedcf1d35 100644 --- a/api/goldens/v1/aptos_api__tests__accounts_test__test_get_account_resources_returns_empty_array_for_account_has_no_resources.json +++ b/api/goldens/v1/aptos_api__tests__accounts_test__test_get_account_resources_returns_empty_array_for_account_has_no_resources.json @@ -37,7 +37,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "1", + "height": "0", "new_block_events": { "counter": "1", "guid": { diff --git a/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_output_user_transaction_with_script_function_payload.json b/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_output_user_transaction_with_script_function_payload.json index 7045c711768ca..59c4501c3355c 100644 --- a/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_output_user_transaction_with_script_function_payload.json +++ b/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_output_user_transaction_with_script_function_payload.json @@ -21,7 +21,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "2", + "height": "1", "new_block_events": { "counter": "2", "guid": { diff --git a/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_returns_last_page_when_start_version_is_not_specified.json b/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_returns_last_page_when_start_version_is_not_specified.json index 149ea0831e84c..cad9e736791fb 100644 --- a/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_returns_last_page_when_start_version_is_not_specified.json +++ b/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_returns_last_page_when_start_version_is_not_specified.json @@ -194,7 +194,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "14", + "height": "13", "new_block_events": { "counter": "14", "guid": { @@ -468,7 +468,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "15", + "height": "14", "new_block_events": { "counter": "15", "guid": { @@ -742,7 +742,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "16", + "height": "15", "new_block_events": { "counter": "16", "guid": { @@ -1016,7 +1016,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "17", + "height": "16", "new_block_events": { "counter": "17", "guid": { @@ -1290,7 +1290,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "18", + "height": "17", "new_block_events": { "counter": "18", "guid": { @@ -1564,7 +1564,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "19", + "height": "18", "new_block_events": { "counter": "19", "guid": { @@ -1838,7 +1838,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "20", + "height": "19", "new_block_events": { "counter": "20", "guid": { @@ -2112,7 +2112,7 @@ }, "data": { "epoch_internal": "86400000000", - "height": "21", + "height": "20", "new_block_events": { "counter": "21", "guid": { diff --git a/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_with_start_version_is_too_large.json b/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_with_start_version_is_too_large.json index 6df0b4de370db..b640d6f7cc59e 100644 --- a/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_with_start_version_is_too_large.json +++ b/api/goldens/v1/aptos_api__tests__transactions_test__test_get_transactions_with_start_version_is_too_large.json @@ -1,5 +1,5 @@ { - "message": "Given start value (1000000) is higher than the highest ledger version, it must be < 0", + "message": "Given start value (1000000) is higher than the current ledger version, it must be < 0", "error_code": "InvalidStartParam", "aptos_ledger_version": null } diff --git a/api/src/poem_backend/page.rs b/api/src/poem_backend/page.rs index 99923ba1e9c0f..eae25ed8104f3 100644 --- a/api/src/poem_backend/page.rs +++ b/api/src/poem_backend/page.rs @@ -22,7 +22,7 @@ impl Page { let start = self.start.unwrap_or(default); if start > max { return Err(E::bad_request_str(&format!( - "Given start value ({}) is higher than the highest ledger version, it must be < {}", + "Given start value ({}) is higher than the current ledger version, it must be < {}", start, max )) .error_code(AptosErrorCode::InvalidStartParam)); diff --git a/api/src/poem_backend/state.rs b/api/src/poem_backend/state.rs index 8ebe650992eda..0aa78682afd69 100644 --- a/api/src/poem_backend/state.rs +++ b/api/src/poem_backend/state.rs @@ -165,7 +165,7 @@ impl StateApi { let resource = state_view .as_move_resolver() - .as_converter() + .as_converter(self.context.db.clone()) .try_into_resource(&resource_type, &bytes) .context("Failed to deserialize resource data retrieved from DB") .map_err(BasicErrorWith404::internal)?; @@ -230,7 +230,7 @@ impl StateApi { let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?; let resolver = state_view.as_move_resolver(); - let converter = resolver.as_converter(); + let converter = resolver.as_converter(self.context.db.clone()); let vm_key = converter .try_into_vm_value(&key_type, key.clone()) diff --git a/api/src/poem_backend/transactions.rs b/api/src/poem_backend/transactions.rs index 7bee6df58d9f7..5d241a309d97f 100644 --- a/api/src/poem_backend/transactions.rs +++ b/api/src/poem_backend/transactions.rs @@ -200,12 +200,6 @@ impl TransactionsApi { self.simulate(&accept_type, signed_transaction).await } - // TODO: The previous language around this endpoint used "signing message". - // From what I can tell, all this endpoint is really doing is encoding the - // request as BCS. To your average user (read: not knowledgable about - // cryptography), "signing message" is needlessly confusing, hence the name - // change. Discuss this further with the team. - /// Encode submission /// /// This endpoint accepts an EncodeSubmissionRequest, which internally is a @@ -358,13 +352,13 @@ impl TransactionsApi { .context("Failed to get block timestamp from DB") .map_err(BasicErrorWith404::internal)?; resolver - .as_converter() + .as_converter(self.context.db.clone()) .try_into_onchain_transaction(timestamp, txn) .context("Failed to convert on chain transaction to Transaction") .map_err(BasicErrorWith404::internal)? } TransactionData::Pending(txn) => resolver - .as_converter() + .as_converter(self.context.db.clone()) .try_into_pending_transaction(*txn) .context("Failed to convert on pending transaction to Transaction") .map_err(BasicErrorWith404::internal)?, @@ -451,7 +445,7 @@ impl TransactionsApi { SubmitTransactionPost::Json(data) => self .context .move_resolver_poem()? - .as_converter() + .as_converter(self.context.db.clone()) .try_into_signed_transaction_poem(data.0, self.context.chain_id()) .context("Failed to create SignedTransaction from SubmitTransactionRequest") .map_err(SubmitTransactionError::bad_request), @@ -474,7 +468,7 @@ impl TransactionsApi { MempoolStatusCode::Accepted => { let resolver = self.context.move_resolver_poem()?; let pending_txn = resolver - .as_converter() + .as_converter(self.context.db.clone()) .try_into_pending_transaction_poem(txn) .context("Failed to build PendingTransaction from mempool response, even though it said the request was accepted") .map_err(SubmitTransactionError::internal)?; @@ -554,7 +548,7 @@ impl TransactionsApi { ) -> BasicResult { let resolver = self.context.move_resolver_poem()?; let raw_txn: RawTransaction = resolver - .as_converter() + .as_converter(self.context.db.clone()) .try_into_raw_transaction_poem(request.transaction, self.context.chain_id()) .context("The given transaction is invalid") .map_err(BasicError::bad_request)?; diff --git a/api/src/tests/v1/converter_test.rs b/api/src/tests/v1/converter_test.rs index abf0ad3237eac..19b50f7936f10 100644 --- a/api/src/tests/v1/converter_test.rs +++ b/api/src/tests/v1/converter_test.rs @@ -20,7 +20,7 @@ async fn test_value_conversion() { let state_view = context.latest_state_view(); let resolver = state_view.as_move_resolver(); - let converter = resolver.as_converter(); + let converter = resolver.as_converter(context.db); assert_value_conversion(&converter, "u8", 1i32, VmMoveValue::U8(1)); assert_value_conversion(&converter, "u64", "1", VmMoveValue::U64(1)); diff --git a/api/types/Cargo.toml b/api/types/Cargo.toml index 9b7f5e0f66ee5..2aaab68571ff1 100644 --- a/api/types/Cargo.toml +++ b/api/types/Cargo.toml @@ -14,6 +14,7 @@ anyhow = "1.0.57" async-trait = "0.1.53" bcs = "0.1.3" hex = "0.4.3" +indoc = "1.0.6" mime = "0.3.16" poem = { git = "https://github.com/poem-web/poem", features = ["anyhow", "rustls"] } poem-openapi = { git = "https://github.com/poem-web/poem" } diff --git a/api/types/src/address.rs b/api/types/src/address.rs index 7f717e6c36986..1e90fff7b1ea1 100644 --- a/api/types/src/address.rs +++ b/api/types/src/address.rs @@ -1,7 +1,6 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 -use aptos_openapi::{impl_poem_parameter, impl_poem_type}; use aptos_types::account_address::AccountAddress; use move_deps::move_core_types; use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; @@ -76,9 +75,6 @@ impl<'de> Deserialize<'de> for Address { } } -impl_poem_type!(Address); -impl_poem_parameter!(Address); - #[cfg(test)] mod tests { use crate::address::Address; diff --git a/api/types/src/bytecode.rs b/api/types/src/bytecode.rs index 9ec6e773ecffc..596200a84304a 100644 --- a/api/types/src/bytecode.rs +++ b/api/types/src/bytecode.rs @@ -42,7 +42,7 @@ pub trait Bytecode { fn new_move_struct_field(&self, def: &FieldDefinition) -> MoveStructField { MoveStructField { name: self.identifier_at(def.name).to_owned().into(), - typ: self.new_move_type(&def.signature.0).into(), + typ: self.new_move_type(&def.signature.0), } } @@ -57,10 +57,7 @@ pub trait Bytecode { address: (*self.address_identifier_at(m_handle.address)).into(), module: self.identifier_at(m_handle.name).to_owned().into(), name: self.identifier_at(s_handle.name).to_owned().into(), - generic_type_params: type_params - .iter() - .map(|t| self.new_move_type(t).into()) - .collect(), + generic_type_params: type_params.iter().map(|t| self.new_move_type(t)).collect(), } } @@ -138,13 +135,13 @@ pub trait Bytecode { .signature_at(fhandle.parameters) .0 .iter() - .map(|s| self.new_move_type(s).into()) + .map(|s| self.new_move_type(s)) .collect(), return_: self .signature_at(fhandle.return_) .0 .iter() - .map(|s| self.new_move_type(s).into()) + .map(|s| self.new_move_type(s)) .collect(), } } diff --git a/api/types/src/convert.rs b/api/types/src/convert.rs index b173ec8c1d31b..08ec62f4b9233 100644 --- a/api/types/src/convert.rs +++ b/api/types/src/convert.rs @@ -7,7 +7,6 @@ use crate::{ ModuleBundlePayload, StateCheckpointTransaction, UserTransactionRequestInner, WriteModule, WriteResource, WriteTableItem, }, - wrappers::MoveTypeWrapper, Bytecode, DirectWriteSet, Event, HexEncodedBytes, MoveFunction, MoveModuleBytecode, MoveResource, MoveScriptBytecode, MoveValue, PendingTransaction, ScriptFunctionId, ScriptFunctionPayload, ScriptPayload, ScriptWriteSet, SubmitTransactionRequest, Transaction, @@ -549,7 +548,7 @@ impl<'a, R: MoveResolverExt + ?Sized> MoveConverter<'a, R> { arg_types .into_iter() .map(|t| t.json_type_name()) - .collect::>>()? + .collect::>() .join(", "), args.len(), args, @@ -561,14 +560,10 @@ impl<'a, R: MoveResolverExt + ?Sized> MoveConverter<'a, R> { .map(|(i, (arg_type, arg))| { self.try_into_vm_value(&arg_type.clone().try_into()?, arg) .map_err(|e| { - let expect_str = match &arg_type { - MoveTypeWrapper::Parsed(t) => format!("expect {}", t.json_type_name()), - MoveTypeWrapper::Raw(s) => format!("using {}", s), - }; format_err!( - "parse arguments[{}] failed, {}, caused by error: {}", + "parse arguments[{}] failed, expect {}, caused by error: {}", i, - expect_str, + arg_type.json_type_name(), e, ) }) diff --git a/api/types/src/derives.rs b/api/types/src/derives.rs new file mode 100644 index 0000000000000..0154e59f83230 --- /dev/null +++ b/api/types/src/derives.rs @@ -0,0 +1,216 @@ +// Copyright (c) Aptos +// SPDX-License-Identifier: Apache-2.0 + +//! This file is where we apply a number of traits that allow us to use these +//! traits with Poem. For more information on how these macros work, see the +//! documentation within `crates/aptos-openapi`. +//! +//! For potential future improvements here, see: +//! https://github.com/aptos-labs/aptos-core/issues/2319. + +use aptos_openapi::{impl_poem_parameter, impl_poem_type}; +use serde_json::json; + +use crate::{ + move_types::{MoveAbility, MoveStructValue}, + Address, EventKey, HashValue, HexEncodedBytes, IdentifierWrapper, MoveStructTagWrapper, + MoveType, U128, U64, +}; +use indoc::indoc; + +impl_poem_type!( + Address, + "string", + ( + example = Some(serde_json::Value::String( + "0x88fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1".to_string() + )), + format = Some("hex"), + description = Some("Hex encoded 32 byte Aptos account address") + ) +); + +impl_poem_type!( + EventKey, + "string", + ( + example = Some(serde_json::Value::String( + "0x000000000000000088fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1" + .to_string() + )), + format = Some("hex"), + description = Some(indoc! {" + Event key is a global index for an event stream. + + It is hex-encoded BCS bytes of `EventHandle` `guid` field value, which is + a combination of a `uint64` creation number and account address (without + trimming leading zeros). + + For example, event key `0x000000000000000088fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1` is combined by the following 2 parts: + 1. `0000000000000000`: `uint64` representation of `0`. + 2. `88fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1`: 32 bytes of account address. + "}) + ) +); + +impl_poem_type!(HashValue, "string", ()); + +impl_poem_type!( + HexEncodedBytes, + "string", + ( + example = Some(serde_json::Value::String( + "0x88fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1".to_string() + )), + format = Some("hex"), + description = Some(indoc! {" + All bytes (Vec) data is represented as hex-encoded string prefixed with `0x` and fulfilled with + two hex digits per byte. + + Unlike the `Address` type, HexEncodedBytes will not trim any zeros. + "}) + ) +); + +impl_poem_type!(IdentifierWrapper, "string", ()); + +impl_poem_type!(MoveAbility, "string", ()); + +impl_poem_type!( + MoveStructValue, + "object", + ( + example = Some(json!({ + "authentication_key": "0x0000000000000000000000000000000000000000000000000000000000000001", + "coin_register_events": { + "counter": "0", + "guid": { + "id": { + "addr": "0x1", + "creation_num": "0" + } + } + }, + "self_address": "0x1", + "sequence_number": "0" + })), + description = Some(indoc! {" + This is a JSON representation of some data within an account resource. More specifically, + it is a map of strings to arbitrary JSON values / objects, where the keys are top level + fields within the given resource. + + To clarify, you might query for 0x1::account::Account and see the example data. + + Move `bool` type value is serialized into `boolean`. + + Move `u8` type value is serialized into `integer`. + + Move `u64` and `u128` type value is serialized into `string`. + + Move `address` type value (32 byte Aptos account address) is serialized into a HexEncodedBytes string. + For example: + - `0x1` + - `0x1668f6be25668c1a17cd8caf6b8d2f25` + + Move `vector` type value is serialized into `array`, except `vector` which is serialized into a + HexEncodedBytes string with `0x` prefix. + For example: + - `vector{255, 255}` => `[\"255\", \"255\"]` + - `vector{255, 255}` => `0xffff` + + Move `struct` type value is serialized into `object` that looks like this (except some Move stdlib types, see the following section): + ```json + { + field1_name: field1_value, + field2_name: field2_value, + ...... + } + ``` + + For example: + `{ \"created\": \"0xa550c18\", \"role_id\": \"0\" }` + + **Special serialization for Move stdlib types**: + - [0x1::string::String](https://github.com/aptos-labs/aptos-core/blob/main/language/move-stdlib/docs/ascii.md) + is serialized into `string`. For example, struct value `0x1::string::String{bytes: b\"Hello World!\"}` + is serialized as `\"Hello World!\"` in JSON. + "}) + ) +); + +impl_poem_type!( + MoveType, + "string", + ( + pattern = + Some("^(bool|u8|u64|u128|address|signer|vector<.+>|0x[0-9a-zA-Z:_<, >]+)$".to_string()), + description = Some(indoc! {" + String representation of an on-chain Move type tag that is exposed in transaction payload. + Values: + - bool + - u8 + - u64 + - u128 + - address + - signer + - vector: `vector<{non-reference MoveTypeId}>` + - struct: `{address}::{module_name}::{struct_name}::<{generic types}>` + + Vector type value examples: + - `vector` + - `vector>` + - `vector<0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>>` + + Struct type value examples: + - `0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin> + - `0x1::account::Account` + + Note: + 1. Empty chars should be ignored when comparing 2 struct tag ids. + 2. When used in an URL path, should be encoded by url-encoding (AKA percent-encoding). + "}) + ) +); + +impl_poem_type!( + U64, + "string", + ( + example = Some(serde_json::Value::String("32425224034".to_string())), + format = Some("uint64"), + description = Some(indoc! {" + A string containing a 64-bit unsigned integer. + + We represent u64 values as a string to ensure compatability with languages such + as JavaScript that do not parse u64s in JSON natively. + "}) + ) +); + +impl_poem_type!( + U128, + "string", + ( + example = Some(serde_json::Value::String( + "340282366920938463463374607431768211454".to_string() + )), + format = Some("uint64"), + description = Some(indoc! {" + A string containing a 128-bit unsigned integer. + + We represent u128 values as a string to ensure compatability with languages such + as JavaScript that do not parse u64s in JSON natively. + "}) + ) +); + +impl_poem_parameter!( + Address, + EventKey, + HashValue, + IdentifierWrapper, + HexEncodedBytes, + MoveStructTagWrapper, + U64, + U128 +); diff --git a/api/types/src/event_key.rs b/api/types/src/event_key.rs index f63d410848b1c..31e4f3f8cc6b1 100644 --- a/api/types/src/event_key.rs +++ b/api/types/src/event_key.rs @@ -1,7 +1,6 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 -use aptos_openapi::{impl_poem_parameter, impl_poem_type}; use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{fmt, str::FromStr}; @@ -52,9 +51,6 @@ impl fmt::Display for EventKey { } } -impl_poem_type!(EventKey); -impl_poem_parameter!(EventKey); - #[cfg(test)] mod tests { use crate::event_key::EventKey; diff --git a/api/types/src/hash.rs b/api/types/src/hash.rs index 7c1215ac8c338..9fccbdc55c180 100644 --- a/api/types/src/hash.rs +++ b/api/types/src/hash.rs @@ -1,7 +1,6 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 -use aptos_openapi::{impl_poem_parameter, impl_poem_type}; use serde::{de::Error as _, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fmt, @@ -70,9 +69,6 @@ impl HashValue { } } -impl_poem_type!(HashValue); -impl_poem_parameter!(HashValue); - #[cfg(test)] mod tests { use crate::hash::HashValue; diff --git a/api/types/src/lib.rs b/api/types/src/lib.rs index ad6a1e268fc42..c8e1f0bcf91f6 100644 --- a/api/types/src/lib.rs +++ b/api/types/src/lib.rs @@ -6,6 +6,7 @@ mod address; mod block; mod bytecode; mod convert; +mod derives; mod error; mod event_key; mod hash; diff --git a/api/types/src/move_types.rs b/api/types/src/move_types.rs index 423077a0362bd..61797a95e1e09 100644 --- a/api/types/src/move_types.rs +++ b/api/types/src/move_types.rs @@ -1,9 +1,8 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 -use crate::{wrappers::MoveTypeWrapper, Address, Bytecode, IdentifierWrapper}; +use crate::{Address, Bytecode, IdentifierWrapper}; use anyhow::{bail, format_err}; -use aptos_openapi::{impl_poem_parameter, impl_poem_type}; use aptos_types::{account_config::CORE_CODE_ADDRESS, event::EventKey, transaction::Module}; use move_deps::{ move_binary_format::{ @@ -370,7 +369,7 @@ pub struct MoveStructTag { pub address: Address, pub module: IdentifierWrapper, pub name: IdentifierWrapper, - pub generic_type_params: Vec, + pub generic_type_params: Vec, } impl MoveStructTag { @@ -378,7 +377,7 @@ impl MoveStructTag { address: Address, module: IdentifierWrapper, name: IdentifierWrapper, - generic_type_params: Vec, + generic_type_params: Vec, ) -> Self { Self { address, @@ -403,11 +402,7 @@ impl From for MoveStructTag { address: tag.address.into(), module: tag.module.into(), name: tag.name.into(), - generic_type_params: tag - .type_params - .into_iter() - .map(MoveTypeWrapper::from) - .collect(), + generic_type_params: tag.type_params.into_iter().map(MoveType::from).collect(), } } } @@ -471,6 +466,7 @@ pub enum MoveType { Struct(MoveStructTag), GenericTypeParam { index: u16 }, Reference { mutable: bool, to: Box }, + Unparsable(String), } impl MoveType { @@ -493,6 +489,7 @@ impl MoveType { "string".to_owned() } MoveType::Reference { mutable: _, to } => to.json_type_name(), + MoveType::Unparsable(string) => string.to_string(), } } } @@ -516,6 +513,7 @@ impl fmt::Display for MoveType { write!(f, "&{}", to) } } + MoveType::Unparsable(string) => write!(f, "unparsable<{}>", string), } } } @@ -524,9 +522,8 @@ impl fmt::Display for MoveType { // represent. Internally, it uses parse_type_tag, which cannot handle references // or generic type parameters. This function adds nominal support for references // on top of parse_type_tag, but it still does not work for generic type params. -// As such, all types in the API that expect to work with a MoveType should -// instead work with the MoveTypeWrapper, a class that can represent the parsing -// failure case as well with just the original raw string. +// For that, we have the Unparsable variant of MoveType, so the deserialization +// doesn't fail when dealing with these values. impl FromStr for MoveType { type Err = anyhow::Error; @@ -541,9 +538,13 @@ impl FromStr for MoveType { s = &s[4..]; is_mut = true; } - let inner = parse_type_tag(s) - .map_err(|e| format_err!("parse Move type {:?} failed, caused by error: {}", s, e))? - .into(); + // Previously this would just crap out, but this meant the API could + // return a serialized version of an object and not be able to + // deserialize it using that same object. + let inner = match parse_type_tag(s) { + Ok(inner) => inner.into(), + Err(_e) => MoveType::Unparsable(s.to_string()), + }; if is_ref { Ok(MoveType::Reference { mutable: is_mut, @@ -817,7 +818,7 @@ pub struct MoveStructField { pub name: IdentifierWrapper, #[serde(rename = "type")] #[oai(rename = "type")] - pub typ: MoveTypeWrapper, + pub typ: MoveType, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Object)] @@ -826,10 +827,10 @@ pub struct MoveFunction { pub visibility: MoveFunctionVisibility, pub is_entry: bool, pub generic_type_params: Vec, - pub params: Vec, + pub params: Vec, #[serde(rename = "return")] #[oai(rename = "return")] - pub return_: Vec, + pub return_: Vec, } impl From<&CompiledScript> for MoveFunction { @@ -847,7 +848,7 @@ impl From<&CompiledScript> for MoveFunction { .signature_at(script.parameters) .0 .iter() - .map(|s| script.new_move_type(s).into()) + .map(|s| script.new_move_type(s)) .collect(), return_: vec![], } @@ -1206,16 +1207,6 @@ mod tests { ); } - #[test] - fn test_serialize_deserialize_move_function_non_mut_param() { - test_serialize_deserialize_move_function(false); - } - - #[test] - fn test_serialize_deserialize_move_function_mut_param() { - test_serialize_deserialize_move_function(true); - } - #[test] fn test_parse_invalid_move_script_function_id_string() { assert_eq!( @@ -1278,57 +1269,6 @@ mod tests { test_serialize_deserialize(HexEncodedBytes::from(bytes), json!("0xabcd")) } - // We don't support parsing these right now, so we expect this to end up - // being the raw variant of MoveTypeWrapper. - #[test] - fn test_deserialize_generic_type_param() { - assert_eq!( - MoveTypeWrapper::from_str("0x1::table::Table>").unwrap(), - MoveTypeWrapper::Raw("0x1::table::Table>".to_string()) - ); - } - - fn test_serialize_deserialize_move_function(use_mut_param: bool) { - let param = if use_mut_param { - "&mut signer" - } else { - "&signer" - }; - test_serialize_deserialize( - MoveFunction { - name: IdentifierWrapper::from_str("register").unwrap(), - visibility: MoveFunctionVisibility::Public, - is_entry: false, - generic_type_params: vec![MoveFunctionGenericTypeParam { - constraints: vec![MoveAbility(Ability::Store)], - }], - params: vec![MoveTypeWrapper::Parsed(MoveType::Reference { - mutable: use_mut_param, - to: Box::new(MoveType::Signer), - })], - return_: vec![], - }, - json!({ - "name": "register", - "visibility": "public", - "is_entry": false, - "generic_type_params": [ - { - "constraints": [ - "store" - ] - } - ], - "params": [ - { - "Parsed": param, - } - ], - "return": [], - }), - ); - } - fn test_serialize_deserialize(obj: O, expected: Value) where O: Serialize + DeserializeOwned + PartialEq + Debug, @@ -1406,20 +1346,3 @@ mod tests { serde_json::to_string_pretty(val).unwrap() } } - -// This macro derives all the necessary traits to make it possible to use the -// given types in a Poem API. This macro in many ways is a short cut compared -// to deriving the traits properly (e.g. #[derive(Type)] on a struct), use it -// with great caution, since it essentially rewrites the type to be a string -// from the perspective of the OpenAPI spec, potentially losing some useful -// type information that the client could use. -// See https://github.com/aptos-labs/aptos-core/issues/2319. -impl_poem_type!( - MoveAbility, - MoveStructValue, - MoveType, - HexEncodedBytes, - U64, - U128 -); -impl_poem_parameter!(HexEncodedBytes, U64, U128); diff --git a/api/types/src/wrappers.rs b/api/types/src/wrappers.rs index 19905d576d5d3..1ceca90266c8a 100644 --- a/api/types/src/wrappers.rs +++ b/api/types/src/wrappers.rs @@ -10,23 +10,13 @@ //! just strings, using the FromStr impl to parse the path param. They can //! then be unpacked to the real type beneath. -use crate::{MoveStructTag, MoveType}; +use crate::MoveStructTag; -use anyhow::{anyhow, Result}; -use aptos_openapi::{impl_poem_parameter, impl_poem_type}; -use move_deps::move_core_types::{ - identifier::{IdentStr, Identifier}, - language_storage::TypeTag, -}; +use move_deps::move_core_types::identifier::{IdentStr, Identifier}; -use poem_openapi::{NewType, Union}; +use poem_openapi::NewType; use serde::{Deserialize, Serialize}; -use std::{ - convert::{From, Into, TryFrom}, - fmt, - ops::Deref, - str::FromStr, -}; +use std::{convert::From, fmt, ops::Deref, str::FromStr}; #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)] pub struct IdentifierWrapper(pub Identifier); @@ -77,9 +67,6 @@ impl fmt::Display for IdentifierWrapper { } } -impl_poem_type!(IdentifierWrapper); -impl_poem_parameter!(IdentifierWrapper); - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, NewType)] #[oai(from_parameter = false, from_multipart = false, to_header = false)] pub struct MoveStructTagWrapper(pub MoveStructTag); @@ -109,81 +96,3 @@ impl fmt::Display for MoveStructTagWrapper { MoveStructTag::fmt(&self.0, f) } } - -impl_poem_parameter!(MoveStructTagWrapper); - -// Currently it is not possible to deserialize certain MoveTypes, such as -// generic type params. In those cases, we give up on parsing them as -// MoveTypes and just store the original string representation. This type is -// a painful necessity, we should try to remove it as soon as it becomes -// possible to do so, perhaps as part of removing all the move type conversion -// at the API layer. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Union)] -pub enum MoveTypeWrapper { - Parsed(MoveType), - Raw(String), -} - -impl FromStr for MoveTypeWrapper { - type Err = anyhow::Error; - - fn from_str(s: &str) -> anyhow::Result { - match MoveType::from_str(s) { - Ok(move_type) => Ok(Self::Parsed(move_type)), - Err(_) => Ok(Self::Raw(s.to_string())), - } - } -} - -impl From for MoveTypeWrapper { - fn from(value: MoveType) -> MoveTypeWrapper { - Self::Parsed(value) - } -} - -impl fmt::Display for MoveTypeWrapper { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Parsed(move_type) => move_type.fmt(f), - Self::Raw(s) => write!(f, "{}", s), - } - } -} - -impl From for MoveTypeWrapper { - fn from(tag: TypeTag) -> Self { - Self::Parsed(MoveType::from(tag)) - } -} - -impl TryFrom for TypeTag { - type Error = anyhow::Error; - fn try_from(move_type_wrapper: MoveTypeWrapper) -> anyhow::Result { - match move_type_wrapper { - MoveTypeWrapper::Parsed(move_type) => Ok(TypeTag::try_from(move_type)?), - MoveTypeWrapper::Raw(raw) => Err(anyhow!( - "Could not parse type tag from raw move type: {}", - raw - )), - } - } -} - -impl MoveTypeWrapper { - pub fn json_type_name(&self) -> Result { - match self { - MoveTypeWrapper::Parsed(move_type) => Ok(move_type.json_type_name()), - MoveTypeWrapper::Raw(raw) => Err(anyhow!( - "Could not get json type name from raw move type string: {}", - raw - )), - } - } - - pub fn is_signer(&self) -> bool { - match self { - MoveTypeWrapper::Parsed(move_type) => move_type.is_signer(), - MoveTypeWrapper::Raw(_raw) => false, - } - } -} diff --git a/crates/aptos-openapi/src/helpers.rs b/crates/aptos-openapi/src/helpers.rs index e132ff7e8fb20..f8c481be10708 100644 --- a/crates/aptos-openapi/src/helpers.rs +++ b/crates/aptos-openapi/src/helpers.rs @@ -1,16 +1,66 @@ // Copyright (c) Aptos // SPDX-License-Identifier: Apache-2.0 -/// This macro helps implement necessary traits for a type to be used in -/// a struct that is used in a poem-openapi server. Where possible, prefer -/// to use the existing poem-openapi macros such as Object, NewType, etc. -/// This macro erases the type information, instead returning the data as -/// a string in its JSON representation. For newtypes wrapping strings, -/// this is perfect, but otherwise this is a bit scary, so use it with caution. +//! In order to use a type with poem-openapi, it must implement a certain set +//! of traits, depending on the context in which you want to use the type. +//! For example, if you want to use a type in a struct that you return from +//! an endpoint, it must implement Type. Normally you get this by deriving +//! traits such as Object, Enum, Union, etc. However, in some cases, it is +//! not feasible to use these derives. +//! +//! - The type is outside of reach, e.g. in a crate in aptos-core that is +//! too unrelated, or even worse, in a totally different crate (the move +//! types are a great example of this). +//! - The type is not expressible via OpenAPI. For example, an enum that +//! has some enum variants with values and others without values.This is +//! not allowed in OpenAPI, types must be either unions (variants with +//! values) or enums (variants without values). +//! - We would prefer to serialize the data differently than its standard +//! representation. HexEncodedBytes is a good example of this. Internally, +//! this is a Vec, but we know it is hex and prefer to represent it as +//! a 0x string. +//! +//! For those cases, we have these macros. We can use these to implement the +//! necessary traits for using these types with poem-openapi, without using +//! the derives. +//! +//! Each macro explains itself in further detail. + +/// This macro allows you to use a type in a request / response type for use +/// with poem-openapi. In order to use this macro, your type must implement +/// Serialize and Deserialize, so we can encode it as JSON / a string. +/// +/// With this macro, you can express what OpenAPI type you want your type to be +/// expressed as in the spec. For example, if your type serializes just to a +/// string, you likely want to invoke the macro like this: +/// +/// impl_poem_type!(MyType, "string", ()); +/// +/// If your type is more complex, and you'd rather it become an "object" in the +/// spec, you should invoke the macro like this: +/// +/// impl_poem_type!(MyType, "object", ()); +/// +/// This macro supports applying additional information to the generated type. +/// For example, you could invoke the macro like this: +/// +/// impl_poem_type!( +/// HexEncodedBytes, +/// "string", +/// ( +/// example = Some(serde_json::Value::String( +/// "0x88fbd33f54e1126269769780feb24480428179f552e2313fbe571b72e62a1ca1".to_string())), +/// description = Some("A hex encoded string"), +/// ) +/// ); +/// +/// To see what different metadata you can apply to the generated type in the +/// spec, take a look at MetaSchema here: +/// https://github.com/poem-web/poem/blob/master/poem-openapi/src/registry/mod.rs #[macro_export] macro_rules! impl_poem_type { - ($($ty:ty),*) => { - $( + ($ty:ty, $spec_type:literal, ($($key:ident = $value:expr),*)) => { + impl ::poem_openapi::types::Type for $ty { const IS_REQUIRED: bool = true; @@ -22,8 +72,38 @@ macro_rules! impl_poem_type { format!("string({})", stringify!($ty)).into() } + // We generate a MetaSchema for our type so we can use it as a + // a reference in `schema_ref`. The alternative is `schema_ref` + // generates its own schema there and uses it inline, which leads + // to lots of repetition in the spec. + // + // For example: + // + // gas_unit_price: + // $ref: "#/components/schemas/U64" + // + // Which refers to: + // + // components: + // U64: + // type: string + // pattern: [0-9]+ + fn register(registry: &mut poem_openapi::registry::Registry) { + registry.create_schema::(stringify!($ty).to_string(), |_registry| { + #[allow(unused_mut)] + let mut meta_schema = poem_openapi::registry::MetaSchema::new($spec_type); + $( + meta_schema.$key = $value; + )* + meta_schema + }) + } + + // This function determines what the schema looks like when this + // type appears in the spec. In our case, it will look like a + // a reference to the type we generate in the spec. fn schema_ref() -> ::poem_openapi::registry::MetaSchemaRef { - ::poem_openapi::registry::MetaSchemaRef::Inline(Box::new(::poem_openapi::registry::MetaSchema::new_with_format("string", stringify!($ty)))) + ::poem_openapi::registry::MetaSchemaRef::Reference(format!("{}", stringify!($ty))) } fn as_raw_value(&self) -> Option<&Self::RawValueType> { @@ -56,14 +136,12 @@ macro_rules! impl_poem_type { ::poem::http::HeaderValue::from_str(&string).ok() } } - - )* }; } -// This macro implements the traits necessary for using a type as a parameter -// in a poem-openapi endpoint handler, specifically as an argument like Path. -// A type must impl FromStr for this to work, hence why it is a seperate macro. +/// This macro implements the traits necessary for using a type as a parameter +/// in a poem-openapi endpoint handler, specifically as an argument like Path. +/// A type must impl FromStr for this to work, hence why it is a seperate macro. #[macro_export] macro_rules! impl_poem_parameter { ($($ty:ty),*) => { @@ -110,9 +188,9 @@ mod test { #[test] fn test() { - impl_poem_type!(This); + impl_poem_type!(This, "string", ()); - impl_poem_type!(That); + impl_poem_type!(That, "string", ()); impl_poem_parameter!(That); } } diff --git a/ecosystem/indexer-server/typescript/package.json b/ecosystem/indexer-server/typescript/package.json index 93b0528d1d7e7..8418a3b4bcb02 100644 --- a/ecosystem/indexer-server/typescript/package.json +++ b/ecosystem/indexer-server/typescript/package.json @@ -7,10 +7,10 @@ "start": "node dist/server", "clean": "rm -rf dist", "build": "npm -s run clean && npm -s run generate && tsc", - "generate": "npm -s run generate:prisma && npx openapi-typescript ../doc/v0/openapi.yaml --output src/openapi-schema.ts", - "generate:openapi-express": "java -jar openapi-generator-cli-5.4.0.jar generate -g nodejs-express-server -i ../doc/v0/openapi.yaml -o src/openapi-express", + "generate": "npm -s run generate:prisma && npx openapi-typescript ../doc/openapi.yaml --output src/openapi-schema.ts", + "generate:openapi-express": "java -jar openapi-generator-cli-5.4.0.jar generate -g nodejs-express-server -i ../doc/openapi.yaml -o src/openapi-express", "generate:prisma": "prisma generate", - "generate:openapi": "npx openapi-typescript ../doc/v0/openapi.yaml --output src/openapi-schema.ts", + "generate:openapi": "npx openapi-typescript ../doc/openapi.yaml --output src/openapi-schema.ts", "test": "jest", "lint": "eslint src", "lint:fix": "eslint src --fix"