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

[Aptos]: Fix an issue in deserializing the EntryFunction object #4011

Merged
merged 3 commits into from
Oct 16, 2024

Conversation

10gic
Copy link
Contributor

@10gic 10gic commented Sep 5, 2024

Description

There is a bug in the function serialize_argument, which leads to the deserialization process generating an incorrect EntryFunction object.

How to test

Run Rust, C++, Kotlin, Swift tests

Types of changes

Checklist

  • Create pull request as draft initially, unless its complete.
  • Add tests to cover changes as needed.
  • Update documentation as needed.
  • If there is a related Issue, mention it in the description.

If you're adding a new blockchain

  • I have read the guidelines for adding a new blockchain.

@10gic
Copy link
Contributor Author

10gic commented Sep 5, 2024

Hi @satoshiotomakan , I have come across a bug in the code. Whenever you have some time, could you please review it?

@satoshiotomakan
Copy link
Collaborator

Hi @10gic, thank you for the fix! Could you please add a test with the following steps:

  1. Deserializes an encoded EntryFunction from a byte array/hex
  2. Serializes the decoded EntryFunction
  3. Asserts that the source encoded and encoded at the 2) step byte arrays/hexes are equal

@10gic
Copy link
Contributor Author

10gic commented Sep 5, 2024

Hi @10gic, thank you for the fix! Could you please add a test with the following steps:

  1. Deserializes an encoded EntryFunction from a byte array/hex
  2. Serializes the decoded EntryFunction
  3. Asserts that the source encoded and encoded at the 2) step byte arrays/hexes are equal

Hi @satoshiotomakan, there is no function to deserialize an EntryFunction from a byte array or hex, and the bug exists in the process of deserializing an EntryFunction from JSON.

EntryFunction  -----Serialize 1--> Hex1
               \
                ----Serialize 2-->  Json  --Deserialize[bug here]--> EntryFunction --Serialize--> Hex2

I added a test case to check the equality of Hex1 and Hex2. If the bug is not fixed, Hex1 will not equal Hex2.

@Milerius
Copy link
Collaborator

Milerius commented Sep 5, 2024

Hi @10gic, thank you for the fix! Could you please add a test with the following steps:

  1. Deserializes an encoded EntryFunction from a byte array/hex
  2. Serializes the decoded EntryFunction
  3. Asserts that the source encoded and encoded at the 2) step byte arrays/hexes are equal

Hi @satoshiotomakan, there is no function to deserialize an EntryFunction from a byte array or hex, and the bug exists in the process of deserializing an EntryFunction from JSON.

EntryFunction  -----Serialize 1--> Hex1
               \
                ----Serialize 2-->  Json  --Deserialize[bug here]--> EntryFunction --Serialize--> Hex2

I added a test case to check the equality of Hex1 and Hex2. If the bug is not fixed, Hex1 will not equal Hex2.

Hey @10gic please take a look at this issue: aptos-labs/aptos-core#6235 - I do think that the code is needed.

@10gic
Copy link
Contributor Author

10gic commented Sep 6, 2024

Hi @10gic, thank you for the fix! Could you please add a test with the following steps:

  1. Deserializes an encoded EntryFunction from a byte array/hex
  2. Serializes the decoded EntryFunction
  3. Asserts that the source encoded and encoded at the 2) step byte arrays/hexes are equal

Hi @satoshiotomakan, there is no function to deserialize an EntryFunction from a byte array or hex, and the bug exists in the process of deserializing an EntryFunction from JSON.

EntryFunction  -----Serialize 1--> Hex1
               \
                ----Serialize 2-->  Json  --Deserialize[bug here]--> EntryFunction --Serialize--> Hex2

I added a test case to check the equality of Hex1 and Hex2. If the bug is not fixed, Hex1 will not equal Hex2.

Hey @10gic please take a look at this issue: aptos-labs/aptos-core#6235 - I do think that the code is needed.

Hi @Milerius, you posted an example in that issue:

curl --location --request POST 'https://fullnode.mainnet.aptoslabs.com/v1/transactions/encode_submission' \
--header 'Content-Type: application/json' \
--data-raw '{
  "expiration_timestamp_secs": "199940521552",
  "gas_unit_price": "100",
  "max_gas_amount": "488130",
  "payload": {
    "arguments": [
      "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"
    ],
    "function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50::IFO::release",
    "type": "entry_function_payload",
    "type_arguments": [
      "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD",
      "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI",
      "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1"
    ]
  },
  "sender": "0x2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de9",
  "sequence_number": "75"
}'

I checked the source code of function release in module IFO in contract address 0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50:

image

The type of argument vesting_schedule_id of function release is vector<u8> (not address), so the length is prepended to data when do bcs encoding. Please note there are different rules to encode vector<u8> and address:

address:    not length is prepended when do bcs encoding
vector<u8>: length is prepended when do bcs encoding

The tricky part is that both vector<u8> and address are represented in hex string in the input of RPC /transactions/encode_submission. For example, the data

"arguments": [
      "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"
    ]

can represent the argument of following two functions:

fun release(sender: &signer, vesting_schedule_id: vector<u8>)
fun release(sender: &signer, vesting_schedule_id: address)

We must know the extract function argument type while do bcs encoding.

I think in the implement code of /transactions/encode_submission, it must check the runtime to determinate the type of vesting_schedule_id. Let's look at a simple example:

## I change the function address from 0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d50 to 0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d51, so it would fail
curl --location --request POST 'https://fullnode.mainnet.aptoslabs.com/v1/transactions/encode_submission' \
--header 'Content-Type: application/json' \
--data-raw '{
  "expiration_timestamp_secs": "199940521552",
  "gas_unit_price": "100",
  "max_gas_amount": "488130",
  "payload": {
    "arguments": [
      "0xc95db29a67a848940829b3df6119b5e67b788ff0248676e4484c7c6f29c0f5e6"
    ],
    "function": "0xc23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d51::IFO::release",
    "type": "entry_function_payload",
    "type_arguments": [
      "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::BUSD",
      "0x48e0e3958d42b8d452c9199d4a221d0d1b15d14655787453dbe77208ced90517::coins::DAI",
      "0x9936836587ca33240d3d3f91844651b16cb07802faf5e34514ed6f78580deb0a::uints::U1"
    ]
  },
  "sender": "0x2ce519d8cd60e0870e874e8000e8cbc87c8172e6acdbec83662b4c8cc3fc3de9",
  "sequence_number": "75"
}'
{"message":"The given transaction is invalid: Failed to parse transaction payload: Module ModuleId { address: c23c3b70956ce8d88fb18ad9ed3b463fe873cb045db3f6d2e2fb15b9aab71d51, name: Identifier(\"IFO\") } can't be found","error_code":"invalid_input","vm_error_code":null}

RPC /transactions/encode_submission fails if I provide a incorrect function address. /transactions/encode_submission need function address to determinate the type of argument.

My fix appears to be correct.

fn serialize_argument(arg: &TransactionArgument) -> EncodingResult<Data> {
    match arg {
        TransactionArgument::U8(v) => bcs::encode(v),
        TransactionArgument::U16(v) => bcs::encode(v),
        TransactionArgument::U32(v) => bcs::encode(v),
        TransactionArgument::U64(v) => bcs::encode(v),
        TransactionArgument::U128(v) => bcs::encode(v),
        TransactionArgument::U256(v) => bcs::encode(v),
        TransactionArgument::U8Vector(v) => bcs::encode(v),
        TransactionArgument::Bool(v) => bcs::encode(v),
        TransactionArgument::Address(v) => bcs::encode(v),   //  Double BCS encoding is not necessary for the address type.
    }
}

However, deserializing EntryFunction from JSON in the wallet core (or any other offline tool) remains ambiguous, as we need the function argument type for clarity.

@10gic
Copy link
Contributor Author

10gic commented Sep 7, 2024

As I said, deserializing EntryFunction from JSON is ambiguous. So following line would lead to bugs.

let entry_function = EntryFunction::try_from(v)?;

I believe that issue #3899 is also caused by this bug.

Possible solution: pass types of arguments into wallet core, use these types of information to reconstruct EntryFunction.

@satoshiotomakan satoshiotomakan merged commit 2b64596 into trustwallet:master Oct 16, 2024
12 checks passed
vcoolish pushed a commit that referenced this pull request Nov 1, 2024
* Fix an issue in deserializing the EntryFunction object

* Add more test cases

---------

Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants